見出し画像

Java-24(演習問題)

●勉強

▶️問題1

抽象クラス、抽象メソッドに関するもの
インターフェース、実装に関するもの
パッケージ、インポートに関するもの
参照型の型変換に関するもの

//タクシークラス

public class Taxi{
  private int crewNum;
  
  public Taxi(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){
    System.out.println("タクシーの乗客:" + crewNum + "名");
  }
}
//電車クラス

public class Train{
  private int crewNum;

  public Train(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){
    System.out.println("電車の乗客:" + crewNum + "名");
  }
}
//タクシークラス

public class Taxi extends Vehicle{
  private int crewNum;    //共通
  
  public Taxi(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){      //共通
    System.out.println("タクシーの乗客:" + crewNum + "名");
  }
}
//電車クラス

public class Train extends Vehicle{
  private int crewNum;      //共通

  public Train(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){       //共通
    System.out.println("電車の乗客:" + crewNum + "名");
  }
}

共通部分をまとめて、乗り物を管理する抽象クラスとして、Vehicleクラスを定義する。Vehicleとは、乗り物。内容がないメソッドは抽象メソッドとしてabstractを入れて、それが入ったクラスはabstractを入れれば良いはず・・!

まずVehicleクラスを作ろう。

public abstract class Vehicle{
  protected int crewNum;  //乗客人数(メンバ変数)

  public abstract void showCrewNum(){    //変数crewNumの値を表示する(抽象メソッド) 
  }
}

Vehicleクラスをもとにして、Busクラスを作成したい。
Busクラスは、Vehicleクラスを継承しているので、extendsを代入している。

public class Bus extends Vehicle{
  public Bus(int crewNum){
  }
  public void showCrewNum(){
  }
}

▶️参考

・2種類の継承

これまでの継承では、例えばスーパークラスのPersonクラスにサブクラスのStudentクラス(学籍番号などを追加)などの機能追加を考えていた。
もう一つは、サブクラスをもとに、スーパークラスを考える方法である。
サブクラスの共通をまとめてスーパークラスを作る考え方である。

メンバ変数や定義と実装内容が全て同じ場合は、そのまま。
定義のみであれば、定義のところだけを、クラスの中に入れられる。
このようなものを、抽象メソッドという。

・抽象クラスとポリモフィズム

実装内容を持たないメソッドを抽象メソッドと呼び、修飾子のところに、abstractをつける。abstract(抽象的な)
抽象メソッドを持つクラスを抽象クラスとも呼ぶ。これも修飾子の所に、abstracrtをつける。インスタンス化は不可である。

・サンプルプログラム

public abstract class Club{
  private String name;     // 部活名

  public Club(String name){     //コンストラクタ
    this.name = name;
  }
  public void display(){       //displayメソッド  
    System.out.println("部活動:" + name);
  }
  public abstract void practice();       //practiceメソッド→メソッドの定義だけしている。
}

practiceは、メソッドの定義のみしているので、曖昧→抽象メソッドと呼ばれる。抽象メソッドなので、abstractというキーワードを入れる。
クラス定義の中にも、同様に入れること。

public class TandF extends Club{
  public TandF(String name){  //TandFのコンストラクタを定義している。
    super(name);
  }
  public void practice(){    //先ほどの抽象メソッドの中身を実装している。
    System.out.println("ウォームアップ");
    System.out.println("インターバル");
    System.out.println("筋肉トレーニング");
  }
}
public class Football extends Club{
  public Football(String name){   //TandFのコンストラクタを定義している。
    super(name);
  }
  public void practice(){   //先ほどの抽象メソッドの中身を実装している。オーバーライド(スーパークラスのメソッドをサブクラスにて再定義すること)している。
    System.out.println("ドリブル練習");
    System.out.println("シュート練習");
    System.out.println("ミニゲーム");
  }
}
public class Student6{
  private String name;     // 氏名
  private Club club;       // Clubオブジェクト

  public Student6(String name,Club club){ 
    this.name = name;
    this.club = club;
  }
  public void display(){  
    System.out.println("名前:" + name);
    club.display();
  }
  public void practice(){
    club.practice();
  }
}
public class StuSample6{    //実行用クラス
  public static void main(String[] args){   //2つの部活動のオブジェクトを作っている。
    TandF taf = new TandF("陸上競技部");
    Football fb = new Football("サッカー部");
    
    Student6 stu1 = new Student6("菅原",taf);  //一人目の生徒
    stu1.display();
    stu1.practice();
    
    Student6 stu2 = new Student6("桜井",fb);  //サッカー部のオブジェクトをコンストラクタに渡している。
    stu2.display();
    stu2.practice();

  }
}

studentクラスのメソッドは、clubのオブジェクトによって、それぞれ異なる処理をしていた。これをポリモフィズムという。多様態という。

メリットは、
プログラムの書き方が統一できるという点は良い。
これらのクラスを利用する側も助かる。呼び出す側の書き方を統一できる。
→プログラムを拡張していきやすいというメリットがある!

▶️回答

別のバスクラスを作ろうというものだ。提供されているプログラムの共通ぶぶんをまとめて、乗り物クラスを作り、そこからBusクラスを作ろうという流れ。

public abstract class Vehicle{  //抽象クラス
  protected int crewNum;
  public abstract void showCrewNum();   //中身を書かない(抽象メソッド)
}

これを継承できるようにしたいので、

public class Bus extends Vehicle{
  public Bus(int crewNum){   //コンストラクタを定義
    this.crewNum = crewNum;
  }
  
  public void showCrewNum(){
    System.out.println("バスの乗客"+crewNum+"名");
  }
}

▶️問題2

interface Stopable{
//  String MESSAGE;
  void stop();
}
//タクシークラスの編集

public class Taxi extends Vehicle implement Stopable{
  private int crewNum;    //共通
  
  public Taxi(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){      //共通
    System.out.println("タクシーの乗客:" + crewNum + "名");
  }
  void stop(){
    System.out.println("ここで降ります");
  }
}
//電車クラスの編集

public class Train extends Vehicle implement Stopable{
  private int crewNum;      //共通

  public Train(int crewNum){
    this.crewNum = crewNum;
  }
  void showCrewNum(){       //共通
    System.out.println("電車の乗客:" + crewNum + "名");
  }
  void stop(){
    System.out.println("次止まります");
  }
}

▶️参考

・インターフェース

クラスは、メンバ変数とメソッドを持つことができる。
インターフェースは、定数と抽象メソッドを持つことができる。

interface Englishable {
  String LANG = "英語";
  void displayEng();
}

インターフェースを実装(implements)したクラスでは、すべてのメソッドの処理を定義する。

class Student implements Englishable {
  String name;

  void displayEng(){
    System.out.println()
}

インターフェースは、複数実装できる。
継承は、枝を張るイメージ。
実装は、色を塗るイメージ。

継承関係にないもの同士を、抽象クラスとインターフェースを使い分けることが必要になる。

▶️サンプルプログラム

public interface Englishable{  //
  String LANGUAGE = "[英語]";  //定数(finalのついた定数と扱われる)
  void displayEng();                    //メソッド(abstractがついた抽象メソッドとして扱われる)
}
public class Student7 implements Englishable{  //EnglishableクラスのdisplayEng()の中身を実装しなければならない
  private String name;

  public Student7(String name){
    this.name = name;
  }
  public void display(){
    System.out.println("名前:" + name);
  }
  public void displayEng(){
    System.out.println(Englishable.LANGUAGE);
    System.out.println("Name:" + name);
  }
}
public class Baseball implements Englishable{
  public void display(){
    System.out.println("キャッチボール");
    System.out.println("シートノック");
    System.out.println("バッティング");
  }
  public void displayEng(){
    System.out.println(Englishable.LANGUAGE);
    System.out.println("catch ball");
    System.out.println("seat knock");
    System.out.println("batting practice");
  }
}
public class StuSample7{  //実行
  public static void main(String[] args){
    Student7 stu = new Student7("Mike");
    stu.displayEng();
    
    Baseball bb = new Baseball();
    bb.displayEng();
  }
}

メリット
継承関係に全くない別のクラス同士でも、インターフェースを実装すると、
メソッドを統一することができるということ。

▶️回答

class の部分をinterfaceに変更する。
自動的に抽象メソッドとして扱われる。
→実装するクラスを作る。

package mypack;

public class Taxi extends Vehicle implements Stopable{
  public Taxi(int crewNum){
    this.crewNum = crewNum;
  }
  public void showCrewNum(){
    System.out.println("タクシーの乗客:" + crewNum + "名");
  }
  public void stop(){
    System.out.println("ここで降ります");
  }
}

Vheicleクラスを拡張して、Stopableインターフェスを実装するという英文。
stop()メソッドを、Taxi,Busクラスに実装する必要がある。Trainクラスには実装しない。

package mypack;

public class Train extends Vehicle{
  public Train(int crewNum){
    this.crewNum = crewNum;
  }
  public void showCrewNum(){
    System.out.println("電車の乗客:" + crewNum + "名");
  }
}

また、package パッケージ名を追加。
実行用のクラスは、.*;(←これを使うと、すべてを)

//インポート
import mypack.*;

public class UseVehicle{
  public static void main(String[] args){
    Vehicle[] v = new Vehicle[3];
    v[0] = new Taxi(4);     //スーパー = サブなので自動変換
    v[1] = new Train(160);
    v[2] = new Bus(80);

    for(int i = 0; i < v.length; i++){
      v[i].showCrewNum();
      if(v[i] instanceof Stopable){.  //v[i]は、Stopable
        Stopable s = (Stopable)v[i];
        s.stop();
      }
    }
  }
}

●復習

  • データには基本データ型と参照型の2種類がある。

  • 小さなデータ型から大きなデータ型に変換する場合は自動的に型変換が行われるが、逆の場合はキャストが必要になる。

  • クラス間に継承関係がある場合には、型変換が自動的に行われます。

  • サブクラスからスーパークラスへの変換では問題が発生する可能性があり、その場合にはキャストが必要になる。

▶️参照型の型変換とは

型変換する前のクラスと、変換後のクラスの間で継承関係になるときに参照型の型変換ができる。
継承関係にあるクラス同士でオブジェクトの型変換は、

スーパー = サブの時、  自動で行われる。
サブ   = スーパーの時、キャストで明示する。

▶️自動とキャストの違いとその理由

Student stu1 = new Student(...);
Person psn = stu1;

Studentクラス(入れ物の箱が大きい) > Personクラス(小さい)
ここで、stu1で扱える範囲は大きい。
逆に、psnで扱える範囲は小さい。

親クラス
Personクラス    = ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️

子クラス
Studentクラス = ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️ (←ここまでを継承)   + ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️ (←ここまでを追加) 

よって、箱の大きいstu1を、範囲の小さいpsnに入れることは可能。

Student stu1 = new Student(...);
Person psn = stu1;
Student stu2 = (Student)psn;
psn.display();
psn.chgStuNo(・・・);
stu2.chgStuNo(・・・);

以上の中で、

Student stu2 = (Student)psn;

ここでいう、(Student)がキャストになります。

Studentクラスの入っている要素が多い理由は、Personクラスを継承したからである。つまり、要素の少ないPersonクラスがスーパークラス(親クラス)で、要素の多いStudentクラスが参照。

スーパークラスに、サブクラスに代入する時には、自動で行われる。
なぜなら、サブクラスの中の情報には必ずスーパークラスの情報があるから、型変換するには問題はない。

しかし、スーパークラスから、サブクラスに代入する時には、要素がたくさん入った大きな箱へと広げるため、メモリ上に必ずしも存在するとは限らないので、ただ変換するとコンパイルエラーが起きるので、型変換をする時には、キャストが必要である。ということになる。

▶️サンプルプログラム

public class Person5{
  private String name;

  public Person5(String name){
    this.name = name;
  }
  public void display(){
    System.out.println("名前:" + name);
  }
}
public class Student5 extends Person5{
  private int stuNo;

  public Student5(String name, int stuNo){
    super(name);
    this.stuNo = stuNo;
  }
  public void display(){
    super.display();
    System.out.println("学籍番号:" + stuNo);
  }
  public void chgStuNo(int stuNo){
    this.stuNo = stuNo;
  }
}
public class StuSample5{
  public static void main(String[] args){
    Student5 stu1 = new Student5("菅原",1);
    Person5 psn = stu1;
    psn.display();
    // psn.chgStuNo(1001);

    /* 補足:instanceofキーワード
       オブジェクトのクラスを特定する
       対象オブジェクト instanceof クラス名 */
    if(psn instanceof Student5){
      Student5 stu2 = (Student5)psn;
      stu2.chgStuNo(1001);
      stu2.display();
    }
  }
}

▶️オーバーロードとオーバーライド

オーバーロード
・1つのクラスの中で同じ名前のメソッドを複数定義できること。
・クラスにない、同じ名前で引数の型や数が違うメソッドを定義すること。
 同じような機能を持つメソッドであっても引数のデータ型が異なれば別々
 のメソッドを用意する必要がある。

オーバーライド
・スーパークラスとサブクラスでのメソッドの上書きである。
・サブクラスで、スーパークラスのメソッドを再定義する。
 条件は、戻り値の型、メソッド名、引数の型と数が全て同じ場合。

▶️コンストラクタ

オブジェクトの初期化のための特殊なメソッド
名前がクラス名と同じであり、戻り値を持たないこと(voidを設定)
newクラス名(コンストラクタへの引数)があることが条件。
コンストラクタを定義しない→デフォルトコンストラクタが生成。

▶️static

全インスタンス変数が使える
メンバ変数やメソッドを定義する時には、staticを指定する。
クラス名.変数[メソッド]名と書きなおす必要がある。

▶️カプセル化

「アクセス修飾子」でクラス・メンバ変数・メソッドの公開範囲を指定

⦅公開範囲⦆
public
同クラス、同パッケージ、サブクラス、その他
protected
同クラス、同パッケージ、サブクラス
(なし)
同クラス、同パッケージ
private
同クラス
代入前処理ができる、修正範囲を変えやすいというメリットがある。

class Student{
  private int score;
  public void setScore(int s){
  (中略)
  score = s;
  }
}
○ stu.setScore(80);
× stu.score = 80;
//このように、直接代入をすると、コンパイルエラーが出ることになる。

▶️継承

継承関係にある親クラスをスーパークラス、子クラスをサブクラスという。extendで継承可能。

▶️this とsuper

this とsuperによるメソッド・メソッドの呼び出し
this.~ = 自オブジェクトの〜
super.~ = スーパークラスの〜
プログラムの処理を再び書く必要がない。引数名を考えなくて良い。

public class Person3{
  private String name;

  public Person3(String name){
    this.name = name;
  }
  public void display(){
    System.out.println("名前:" + name);
  }
}

this とsuperによるコンストラクタの呼び出し
this()~ = 自オブジェクトの〜
super()~ = スーパークラスの〜
これらは、コンストラクタ内の先頭に記述するようにする。
なければ、自動的にsuper();が設定されることになる。

▶️パッケージ

複数のクラスをまとめる仕組みのこと。まとめかたとしては、
・パッケージ化の宣言
package パッケージ名;

package pack;
public class Student{
          ...
}

このように書くと、実際のフォルダ構成も、パッケージの名前と同じフォルダが作成され、その中にクラスファイルを入れることになる。

注意するべきことは、クラスをパッケージすると、
クラス名も変わるということ。
→pack.Studentクラス(完全修飾名)

・パッケージクラスの利用
しかしながら、いちいちパッケージ名.クラス名を書くのは
importして、パッケージ名を省略できる。

import pack.Student;
class StuSample{
...

パッケージ宣言→import→classへ

参考:クラスライブラリ
java.langパッケージ:基本的なクラス(自動インポート)
java.io:入出力関連
java.net:ネットワーク関連

▶️サンプルプログラム

import pack.Student8;

public class StuSample8{
  public static void main(String[] args){
    Student8 stu = new Student8("菅原");
    stu.display();
  }
}


package pack;

public class Student8{
  private String name;

  public Student8(String name){
    this.name = name;
  }
  public void display(){
    System.out.println("名前:" + name);
  }
}
import java.io.*;

public class WriteFile{
  public static void main(String[] args){
    try{
      BufferedReader r = new BufferedReader(
        new InputStreamReader(System.in));
      PrintWriter w = new PrintWriter(
        new BufferedWriter(new FileWriter("output.txt")));

      String str;
      while((str = r.readLine()) != null){
        w.println(str);
      }
      r.close();
      w.close();
    } catch(IOException e) {
      System.out.println(e);
    }
  }
}

少しずつですが、投稿をしております。 noteで誰かのためになる記事を書いています。 よろしくおねがいします。