【レポート】コピーコンストラクタやクラス型のフィールド

【レポート】コピーコンストラクタやクラス型のフィールド

目的

コピーコンストラクタやクラス型のフィールドを自由に使えるようにする。

内容

第7回の演習で作成したDayクラスを含むMyScheduleクラスを作成してフィールド、コンストラクタ、メソッドを自由に定義し、toStringメソッド、コピーコンストラクタを実装し、他に必要なクラスは独自に実装する。そして、以上の内容をテストするMyScheduleTesterクラスも作成し、実行する。ただし、どこかでクラスインスタンスの配列を作成し、何らかの処理を加えること。また、コピーコンストラクタを使ってインスタンスのコピーを行い何らかの処理を行うこと。

ソースプログラム

以下にDayクラス、MyScheduleクラス、MyScheduleTesterクラスのソースプログラムを以下に示す。

  Dayクラス:

  package lesson7;

public class Day {

  private int year =1;

  private int month=1;

  private int date=1;

  //コンストラクタ

  public Day() {

          System.out.println(“Day()が呼び出されました”);

  }

  public Day(int year) {

          this.year=year;

          System.out.println(“Day(int year)が呼び出されました”);

  }

  public Day(int year,int month) {

          this(year);this.month=month;

          System.out.println(“Day(int year,int month)が呼び出されました”);

  }

  public Day(int year,int month,int date){

          this(year,month);this.date=date;

          System.out.println(“Day(int year,int month,int date)が呼び出されました”);

  }

  public Day(Day d){

          this(d.year,d.month,d.date);

          System.out.println(“Day(Day d)のコピーコンストラクタが呼び出されました”);

  }//Day型の変数b(仮引数ではない)

  //年・月・日を取得

  public int getYear(){

          return year;

  }

  public int getMonth(){

          return month;

  }

  public int getDate(){

                return date;

  }

  //年・月・日を設定

  public void setYear(int year){

          this.year=year;

  }

  public void setMonth(int month){

          this.month=month;

  }

  public void setDate(int date){

          this.date=date;

  }

  //年・月・日を設定する独自のメソッド

  public void set(int year,int month,int date){

          this.year=year;

          this.month=month;

          this.date=date;

  }

  //曜日を求めるメソッド

  public int dayOfWeek(){

          int y=year;

          int m=month;

          if(m==1||m==2){

                  y–;

                  m+=12;

          }

          return (y+y/4-y/100+y/400+(13*m+8)/5+date)%7;

  }

  //日付dと等しいか

  public boolean equalTo(Day d){

          return year==d.year&&month==d.month&&date==d.date;

  }

  //文字列表現を返却

  public String toString(){

          String[] wd={“日”,”月”,”火”,”水”,”木”,”金”,”土”};

          return String.format(“%04d年%02d月%02d日(%s)”,year,month,date,wd[dayOfWeek()]);

  }

}

  MyScheduleクラス

  package lesson7;

public class MySchedule {

private Day playDay;//いつ

    private String place;//どこで

    private int time;//およそ何時間

private String play;//何をするのか

        //コンストラクタ

        public MySchedule(Day playDay,String place,int time,String play ){

                System.out.println(“あ”);

                this.playDay=new Day(playDay);

                System.out.println(“い”);

                this.place=place;

                this.time=time;

                this.play=play;

        }

        public Day getPlayDay(){

                return new Day(playDay);

        }

        public void kaku(){

                System.out.println(“場所:”+place);

                System.out.println(“なん時間するのか:”+time+”時間”);

                System.out.println(“なにをするのか:”+play);      

        }

}

  MyScheduleTesterクラス

   package lesson7;

import java.util.Scanner;

public class MyScheduleTester {

        public static void main(String[] args){

                Scanner stdIn=new Scanner(System.in);

            System.out.println(“年:”);  int y=stdIn.nextInt();

            System.out.println(“月:”);  int m=stdIn.nextInt();

            System.out.println(“日:”);  int d=stdIn.nextInt();

            System.out.println(“どこで:”); String place=stdIn.next();

            System.out.println(“何時間:”); int time=stdIn.nextInt();

            System.out.println(“何をするか:”); String play=stdIn.next();

            MySchedule MS=new MySchedule(new Day(y,m,d), place, time, play);

            MS.kaku();

            System.out.println(“やる日:”+MS.getPlayDay());

            //クラス型のインスタンス配列

            System.out.println(“日付の個数:”);

            int n=stdIn.nextInt();

            Day[] a=new Day[n]; //要素数n個のDay型配列

            for(int i=0;i<a.length;i++){

                 a[i]=new Day(y,m,d);

            }

            for(int i=0;i<a.length;i++){

                 System.out.println(“a[“+i+”]=”+a[i]);

            }

            Day day1=new Day(y,m,d);

            System.out.println(“day1 = “+day1);

            Day day2=new Day(day1);//Dayクラスのコピーコンストラクタがある

            System.out.println(“day2をday1と同じ日付として作りました。”);

            System.out.println(“day2 = ” + day2);

            if (day1.equalTo(day2))

              System.out.println(“day1とday2は等しいです。”);

            else

              System.out.println(“day1とday2は等しくありません。”);

        }

}

実行結果

考察

今回のプログラムはクラスが3つあるが、呼び出される順としては、mainメソッドをもつMyScheduleTesterクラスだ。MyScheduleTesterクラスでは、最初に年を入力するint y=stdIn.nextInt();月を入力するint m=stdIn.nextInt();日を入力するint d=stdIn.nextInt();場所の「どこ」でを入力するString place=stdIn.next();何時間を入力するint time=stdIn.nextInt();何をするかを入力するString play=stdIn.next();を処理する。place 、playは文字列を入力する為、String型だ。そして、これらを引数にとるMyScheduleクラスのインスタンス化、MySchedule MST=new MySchedule(new Day(y,m,d), place, time, play);を生成する。引数の最初にnew Day(y,m,d)と書いてあるが、これはDay型のインスタンスをnewによって生成する式である。生成したインスタンスへの参照がコンストラクタに渡されることになる。インスタンスの生成により、MyScheduleクラスのコンストラクタ

public MySchedule(Day playDay,String place,int time,String play ){

                this.playDay=new Day(playDay);

                this.place=place;

                this.time=time;

                this.play=play;

     }

が呼び出される。ここで、MyScheduleクラスのメソッドにDay型の変数 playDayがある。これは、予定日を格納するためのフィールドがplayDay。このフィールドの型はクラス型だ。Day型のインスタンスではなく、インスタンスを参照する変数である。

 MyScheduleクラスのコンストラクタのthis.playDay=new Day(playDay);はコンストラクタで日付を設定する部分。ここでは、new演算子とコピーコンストラクタを用いて予定日のインスタンスを生成している。仮引数playDayに受け取った日付のコピーを作って、そのコピーへの参照をフィールドplayDayに代入している。そのため、フィールドplayDayは、仮引数に受け取った日付のコピーを参照することになる。このようになっていることは実行結果でも確認可能だ。まず、仮引数playDayに受け取った日付のコピーを作った部分はコンストラクタDay(int year)が呼ばれ、次にDay(int year,int month)が呼ばれ、最後にDay(int year,int month,int date)が呼ばれて終了となっている。ここで、this(…)を使っているが、これをコンストラクタの先頭に書くと同一クラス内のコンストラクタの呼び出しが出来る。仮引数playDayに受け取った日付のコピーを作り終わったら、MyScheduleクラスのコンストラクタの内部に行く。これは、System.out.println(“あ”);が出力されたときに確認出来る。次のthis.playDay=new Day(playDay)では、コンストラクタDay(int year)が呼ばれ、次にDay(int year,int month)が呼ばれ、最後にDay(int year,int month,int date)が呼ばれて、最後にコピーコンストラクタ、Day(Day d)が呼ばれる。もし、this.playDay=new Day(playDay)の部分をnewによってインスタンスを生成せず、this. playDay=playDay;という単純な代入になっていると、参照先が同じになってしまい、新しい日付インスタンスが作られなくなってしまう。これが終わるとMyScheduleクラスのコンストラクタの内部に戻る。これは、System.out.println(“い”);が出力されたときに確認出来る。そのあとは、place,time,playに先ほど、入力した数、文字列が入力され、このコンストラクタは終了で、MyScheduleTesterクラスに戻り、MS.kaku();が呼ばれplace,time,playの出力がされる。そして、その次はMS.getPlayDay()によってgetPlayDay()メソッドが呼び出される。このメソッドのreturn文は、new Day(playDay);と書いてあるが、ここの部分を、play;と書いてしまうと、メソッドgetPlayDay()は、やる日フィールドそのものへの参照を返却してしまい、返却された参照を通じて、外部からやる日フィールドの値が書きかえられる可能性がある。new Day(playDay)にすると、メソッドgetPlayDay()は、やる日フィールドのコピーへの参照を返却する。返却された参照を通じて、外部からやる日フィールドの値が書き換えられることは無くなる。

 そのあとのプログラムは、まずクラス型のインスタンス配列のプログラムで、ここで重要なのがクラス型インスタンスの配列を利用するには、クラス型変数の配列を生成したあとに、個々の要素のインスタンスを生成しなければならないこと。続いての、クラス型変数の比較のプログラムで、Day day2=new Day(day1);の部分で実行結果に書いてある通り、ここでコピーコンストラクタが呼び出される。受け取った日付dのフィールドd.year,d.month,d.dateの値をコピーし、日付を初期化してくれる。

まとめ

 使い捨てのものでない限り、クラスやメソッドにはpublicを付けて宣言するとよい。代入もしくは初期化によって、クラス型変数の値をコピーすると、全フィードの値ではなく、参照先がコピーされる。メソッドの引数としてクラス型変数をやり取りする際は、インスタンスへの参照が受け渡される。コンストラクタを多重定義すれば、そのクラスのインスタンス構築法の選択肢が広がる。コンストラクタの先頭では、同一クラス内にコンストラクタを、this(…)によって呼び出せる。同一クラス型の引数を受け取って、その全フィールドの値をコピーするコピーコンストラクタを、必要に応じて定義するとよい。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です