気まぐれjava学習日記

基本情報処理試験に見るjavaコーディングの個性的なグルメメニューを、オタク目線で隅々まで味わい尽くしたい!

インスタンスはどうして必要なのかしら?をざっくり

2017年05月25日 | 基本情報処理試験Java

次の問題が実際のプログラミングをトレースしていかなくてはいけないのだけど、[プログラム3]のフィールド部に、それぞれ0で初期化したint型変数accumulator, register, operator以外に、CalculatorOutput型の変数があるわよね。このクラス型の変数っていうのが、ちょっとわからない、って人のために、ざっくりインスタンスについて説明しておくわね。

 

そもそもどうしてインスタンスとかオブジェクトってものが必要なのかしら。マイクロソフトのOffice Excelの関数を使う人は多いわよね。いわゆるメソッドとはこの関数を自作するのに似ているのだけど、少なくともExcelというアプリケーションのワークシートの中では、クラスというイメージを意識したことはないわよね。でもVBマクロを書く段階になると、このオブジェクト指向の理解があればかなり効率の良いコードが書けるようになるわね。だから根っこの部分からちゃんと理解しておきたいものね。

 

*****そもそもインスタンスってどうしても必要なものなの?全てクラスメソッドとクラス変数にすれば分かりやすいのに*****

クラスメソッド、いわゆるstaticメソッドは、Excel自作関数に近い使い方ができるわね。但し、メソッドであるからには、同じメソッドの仲間のメインメソッド(実行メソッド)の中には書けないことは分かるわよね。クラスメソッドをメインメソッド(実行メソッド)の中で呼び出すとき、メソッド名だけで呼び出すにはそれなりの環境が必要なのよ。

以下は、Sampleクラス内に、メインメソッド(または実行メソッド)と、hello()メソッド、greeting()メソッド、bye()メソッドが並列に並んでいる場合よ。

class Sample{

   public static void main(String[] args){

      hello();   

//同じSampleクラス内にあるstaticなhello()メソッドなので「staticに参照する」つまりメソッド名だけで呼び出せるの。前置きなしで呼び出せるのは、同一クラス内のstaticなメソッドだけ。

      new Sample().greeting();  

//同じクラス内のメソッドであっても、staticのないメソッドは「クラス名のインスタンス.メソッド名」で呼び出すのよ。

      new Sample().bye();

   }  

   public static void hello(){  System.out.println("M.Antaranetteのブログへようこそ。");   }

   public void greeting(){  System.out.println("javaは読み手に優しいプログラムなのよ。";   }

   public void bye(){  System.out.println("ではまたね。");  }

}

同じクラス内のメインメソッドであっても、staticでない、インスタンスメソッドgreeting()メソッドとbye()メソッドは、「インスタンス+メソッド名」で呼び出すのよ。生々しいけど、分かりやすいように、その都度newしたばかりの新しいインスタンスを使ってみたわ。

class Sample{

   public static void hello(){  System.out.println("M.Antaranetteのブログへようこそ。");   }

   public void greeting(){  System.out.println("javaは読み手に優しいプログラムなのよ。";   }

   public void bye(){  System.out.println("ではまたね。");  }

}

class Exec{

   public static void main(String[] args){

      Sample.hello();    

//別クラスのstaticメソッドを呼び出すときは、「クラス名.メソッド名()」で呼び出すのよ。

      Sample s=new Sample();

      s.greeting();  

//いちいちnewするのは面倒だから、1度だけnewしてインスタンス化して名前を付けてみたわ、「インスタンス名.メソッド名」で呼び出すの。

      s.bye();

   }  

 }

でもね、実はstaticメソッドは万能だから、インスタンスメソッドと同じ呼び出し方もできるのよ。「s.hello();」でも呼び出せるのよ。

どうしても同一インスタンスを使わなくはいけない場面もあるのよ。例えば「名前」、「住所」などの会員情報を扱うクラスを考えたとき、情報として保存登録していつでも取り出せるようにしたい「名前」「住所」がフィールドの変数に保管してもらうことになるっていうのは想像できるでしょ?このとき、作成したクラスのフィールドに直接会員情報を保存することはできないのよ。もしもそんなことができたら、コピークラスをいくつも書いて、ついでにクラス名も同じ名前だと保存できないから、少しずつ変えていかなくてはいけないし、「名前」と「住所」のたった2つだけの情報を保存するために、そんな面倒で、膨大なメモリを消費するプログラムなんてナンセンスよね。オブジェクト指向と言う考え方では、「クラス」を1つ作ると、そのクラスはいわばテンプレートとして機能する「ゴム印」みたいなものだと考えてみて。ゴム印自体には何も書けないけど、スタンプを紙面にペタンと押せば、そこには「名前:」とか「住所:」とかが押印されて、枠とかも作ってあって、書き込みができる申請書みたいなものができるでしょう?そうやってぺたぺたと作った用紙が、ここでいうnewして作るインスタンス、実体ということになるの。「クラス」はゴム印、「実体、インスタンス」は書き込み用紙ということになるかしら。

 

 

インスタンス生成、つまりnewの仕方:

newしたいクラスの中に「クラス名(何らかの引数){」に当たる記述がないとき、「new クラス名 ()」でインスタンスの出来上がり。後で付け足し説明するけど、「new コンストラクタ()」というのが正解なんだけれど。

フィールド変数の見分け方:

メインメソッドや通常のメソッドの外にあって、小文字で始まり()がついていない単語は、フィールド部(掲示板)にある、フィールド変数よ。

 

メソッドの見分け方:

「publicかprivateかprotectedか、無しの場合もあるけどアクセス修飾子と呼ばれるもの」+「intとかStringとかの戻り値の型。付けないといけないわ。voidのときは戻り値なし。それ以外のときは、処理の中で、ここで指定した型の戻り値を「return~;」しなくてはいけないわ」+「小文字で始まるメソッド名。処理内容とかけ離れたタイトルはだめ。これは読み手を意識したマナーなの」+「引数がないとき()、引数があるとき(引数)を付けるの」+「{ 処理 };」の形を取っているわ。

public void hello(){ System.out.println("hello"); } ならば、「publicだから外部から呼び出せて、戻り値なし、引数なしのhello()メソッドで、処理は「hello」と出力して改行します」という意味よ。

private int calculate(int x, int y){  return x+y;  }ならば、「privateだから、同じクラスのメソッドからしか呼び出されないメソッド。int型の戻り値の指定があって、引数のx+yの計算結果を呼び出し元の呼び出した記述の箇所へreturnします」という情報が分かるわね。

 

public class Member{

   private String name, place;   

//メインメソッドや通常のメソッドの外にあって、小文字で始まり()がついていない単語だから、フィールド変数

   public String getName(){ return name;  }  

//ゲッターメソッド。privateなフィールド変数を、呼び出し元の呼び出し行へ伝えるメソッド。外部から直接フィールドの値を読み込めないときに定義するメソッド

   public String getPlace(){  return place;  }  

//ゲッターメソッド。privateなフィールド変数を、呼び出し元の呼び出し行へ伝えるメソッド。外部から直接フィールドの値を読み込めないときに定義するメソッド

   public void setName(String name){  this.name=name;  } 

//セッターメソッド。privateなフィールド変数へ引数を代入するメソッド。外部から直接フィールドへ値を代入できないときに定義するメソッド

   public void setPlace(String place){  this.place=place; }  

//セッターメソッド。privateなフィールド変数へ引数を代入するメソッド。外部から直接フィールドへ値を代入できないときに定義するメソッド

}

Memberクラスは、ゴム印だから、これをペタンと紙に押印して、用紙を作りましょう。その用紙の「name」に「伊東俊彦」と書きましょう。「place」に「夢島1-1-1」って書きましょう。もう一枚ゴム印をペタンと紙に押印して、用紙を作って、その用紙の「name」に「渡辺喜朗」と書きましょう。「place」に「夢島2-3-1」と書きましょう。

public class Exec{

   public static void main(String[] args){

      new Member().setName("伊東俊彦");  new Member().setPlace("夢島1-1-1");

      new Member().setName("渡辺喜朗");  new Member().setPlace("夢島2-3-1");

   }

}

全部名無しのインスタンスに放り込んだわね。これでは、パソコンのメモリのどこかへ放り込んだのはいいけど、名前がついていないからどうやって呼び出すのかしら。それに"伊東俊彦"も"夢島1-1-1"も"渡辺喜朗"も"夢島2-3-1"も全部、4つの別々のインスタンスの「name」だけ、「place」だけに入ってしまったわね。保存する、という作業のとき、必ず保存したものを呼び出すために、インスタンスそのものに名前を付けておかなくてはいけないわね。

public class Exec{

   public static void main(String[] args){

      Member ito=new Member();

      ito.setName("伊東俊彦");  

      ito.setPlace("夢島1-1-1");

      Member watanabe=new Member();

      watanabe.setName("渡辺喜朗");  

      watanabe.setPlace("夢島2-3-1");

   }

}

これでnewしたインスタンスは2つ、つまり用紙は2枚になったわね。1枚の用紙には「ito」っていうタックインデックスを貼り付けて、もう1枚の用紙には「watanabe」っていうタックインデックスを貼り付けたわ。「ito」インデックスがついている用紙の「name」欄には「伊藤俊彦」と書き、「plaec」欄には「夢島1-1-1」と書いたわね。「watanabe」インデックスがついている用紙の「name」欄には「渡辺喜朗」と書き、「place」欄には「夢島2-3-1」と書いたのね。で、後日、「ito」インデックスの「place」欄を「星空2-5-10」に変更したいときは:

 

public class Exec{

   public static void main(String[] args){ ito.setPlace("星空2-5-10");  }

}

メインメソッド(実行メソッド)の中で、「インスタンス名.メソッド名」で変更すればいいわ。でもインスタンスをnewしたときに、同時に「name」と「place」を保存できないかしら、ってことで、いつものnewに引数を持たせると、いきなりセッターを使う手間が省けるというものよね。クラスをnewしてインスタンスを生成するときの、「クラス名()」は、実は「コンストラクタ」と言われるものなの。newするときだけに実行するメソッドの一種なのよ。この「コンストラクタ」はnewするときに引数を持たせることもできるのよ。その代わり、引数を持たせたら、必ずクラスの中でコンストラクタが行う実行処理内容を書かなくてはいけなくなるわ。

public class Member{

   private String name, place;

   public Member(String name, String place){  this.name=name;  this.place=place;  }

ここでは、引数の「name」と「place」をそれぞれフィールドの「name」と「place」に代入しているわね。これがあると、メインメソッド(実行メソッド)から「name」「place」の初回登録する時だけ、セッターメソッドを使わなくてもよくなるから、コード行数を4行も減らすことができたわね。これもエコ技よ。

public class Exec{

   public static void main(String[] args){

      Member ito=new Member("伊東俊彦", "夢島1-1-1");

      Member watanabe=new Member("渡辺喜朗", "夢島2-3-1");

   }

}

気付いたかしら。newするときの「クラス名()」も「クラス名(引数)」も同じコンストラクタなんだ、ってことに。メインメソッドで、「new Member("伊東俊彦", "夢島1-1-1")」のようなインスタンスが作れるようにするには、Memberクラスで「public Member(String name, String place)」のように、引数有のコンストラクタを定義しなくてはいけないのだけど、「new Member()」のときは定義しなくて良かったのは何故かしら。それはね、コンストラクタが1つもMemberクラスの中で定義されていないとき、javaは「Member(){ }」という、何も処理をしないコンストラクタ(デフォルトコンストラクタ)をひそかに作ってくれているからなのよ。

public class Member{

   private String name, place;

   public Member(String name, String place){  this.name=name;  this.place=place;  }

   public String getName(){ return name;  }  

   public String getPlace(){  return place;  }  

   public void setName(String name){  this.name=name;  } 

   public void setPlace(String place){  this.place=place; }

 public void hello(){  System.out.println("hello");  }

}

ここでMemberクラスの「name」と「place」フィールドに変数を送らないで、ただhello()メソッドを呼び出したいので:

public class Exec{

   public static void main(String[] args){

      Member m=new Member();

      m.hello();

   }

}

としたら「Member m=new Member();」の行でコンパイルエラーになるわね。引数有のコンストラクタを定義してしまうと、引数なしのデフォルトコンストラクタは自動で内部生成してくれなくなるのよ。このときばかりは、{ }だけで、中身のないコンストラクタを定義してあげなくちゃね。

public class Member{

   private String name, place;

   public Member(String name, String place){  this.name=name;  this.place=place;  }

   public Member(){ }

のようにね。ここまで来てインスタンスがなぜ必要かお判りいただけたかしら。

 

「インスタンスは、コンストラクタがクラス内で定義されていない時は「new クラス名()」で作ることができるわ。これは、引数のないコンストラクタをnewしてインスタンスを作っているのと同じことよ」

「コンストラクタを定義するときは、フィールド変数に値を代入して初期化する等々、好きな処理を書いてもいいのよ。但し、一度定義してしまうと、引数なしのデフォルトコンストラクタは、自動で作成されなくなってしまうから、必要に応じて、引数のないコンストラクタを定義しておくのね。そして、コンストラクタをnewする目的は、インスタンスを新しく生成すること。だから逆に言えば、コンストラクタは、インスタンス誕生のときにしか使えないのよ」

「そしてコンストラクタをnewして生成したインスタンスには、生身の値がフィールド変数に入れられるようになるの。また、メソッドにアクセスするためにだけ、インスタンスを生成することもあるのよ。逆を言えば、インスタンスがなくては、フィールドの値を読み込むことも、メソッドを使うこともできないんだから、インスタンスのないJavaなんて考えられないわよ」

public class Member{

   private String name, place;

   public Member(String name, String place){  this.name=name;  this.place=place;  }

   public Member(){  System.out.println("引数のないコンストラクタは受け付けられません");  }

}

引数の有り無しのコンストラクタを作ってみたわ。

public class Exec{

   public static void main(String[] args){

      Member m=new Member();

   }

}

「Member m」はあってもなくても、Member()の処理が実行されて「引数のないコンストラクタは受け付けられません」って出力されるわよ。

因みに、「System.out.print~」を処理として記述したとき、通常のメソッドの場合は、戻り値はなし、つまり「void」にしてね。でもコンストラクタには戻り値の指定は不要よ。そしてもう1つ、コンストラクタが通常のメソッドと見た目が異なる特徴として、名前に大文字で始まるクラス名を使っていることよ。そしてコンストラクタはこの例のように、通常のメソッドのように処理させることもできるのよ。但し、インスタンスを誕生させるとき1度だけしか使えない技だけれどもね。

 

ジャンル:
ウェブログ
この記事についてブログを書く
« 2004(H16)年秋実施、問12を... | トップ | フィールド部に鎮座する「ク... »
最近の画像もっと見る