|
|
|||||||||||||||||||||||||||||
| メンバ変数とメンバメソッド |
H.Kamifuji . |
|
ここではクラスを構成するメンバ変数とメンバメソッドについて見ていきます。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
|
|
ここではメンバ変数について見ておきましょう。メンバ変数とはクラス定義の中で下記の部分です。
class Television{
int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
クラス定義をしている"{"と"}"の中に記述され、そしてメソッドの外に記述されている変数のことです。変数は、変数の定義が記述されている箇所を含んでいる一番近くの"{"と"}"の中でだけ使う事が出来ます。例えば下記の場合で考えてみます。
class Television {
void setChannel(int newChannelNo){
int no = 10;
if (no == 10){
String str = "チャンネルは";
System.out.println(str + no + " です");
}
}
}
少し変なサンプルですが気にしないで下さい。上記で変数「no」が定義されて部分を含んでいる一番近くの"{"と"}"はsetChannelメソッドを定義している"{"と"}"ですので、この変数はsetChannelメソッド内で自由に使うことが出来ます。また変数「str」はif文の"{"と"}"の中に記述されていますので、この変数はif文の中では自由に使えますが、if文の外ではこの変数を使うことは出来ません。ここで最初に記述したクラス定義をもう一度見てください。
class Television{
int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
変数「channelNo」の定義されている部分を含んでいる一番近くの"{"と"}"はクラス定義の"{"と"}"ですので、この変数はクラス内であれば自由に使うことが出来ます。このような変数をメンバ変数と言います。このクラスからオブジェクトを作成した時にメンバ変数は作成され、オブジェクトが破棄されるまで変数に格納されている値は保存されています。また変数の値を格納する領域はオブジェクト毎に別々になっていますので、オブジェクト毎にオブジェクトが破棄されるまで変数に値を保存しておくことができます。 その為、メンバ変数はオブジェクトの性質や状態などを保存しておく時に利用します。上記のクラスの場合で言えば、テレビを表すクラスの中で、そのクラスから作成されたオブジェクトが現在表示しているチャンネルを保存しているために使われています。 |
|
ここではクラスの中に定義したメンバ変数へ値を格納する方法について見ていきます。 まずメンバ変数が定義されている同じクラスの中で値を格納する場合です。下記の例を見てください。
class Television{
int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
上記のsetChannelメソッド内で、メンバ変数に値を格納しています。この場合は、メンバ変数に他の変数の値を格納していますが、メンバ変数に直接値を格納する場合でも同じです。このように同じクラスの中であれば、普通の変数と同じように扱うことが出来ます。またより丁寧に書く場合には、「自分自身のクラス」にあるメンバ変数という意味を込めて、「this.メンバ変数 = 値」と言う記述もします。「this」と言うのは自分自身という意味をもつ定義済みの値で、thisを付けた記述方法の方が正式なのですが通常は省略しています。 次にこのクラスからオブジェクトを作成し、そのオブジェクトを使って値を格納する方法です。下記の例を見てください。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.channelNo = 1;
}
}
class Television{
int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
上記の例では、クラスを元にオブジェクトを作成してから、そのオブジェクトを使ってメンバ変数に値を格納しています。この場合の記述方法は下記のようになります。オブジェクト名.メンバ変数名 = 値;普通の変数の場合は、「変数名 = 値」と言う形で値を格納できますが、メンバ変数の場合は作成されたオブジェクト毎に変数の格納する場所が異なりますので、「オブジェクト名.メンバ変数名 = 値」という形で、どのオブジェクトのメンバ変数かを指定して値を格納します。 最後にメンバ変数が定義されているクラスのメソッドを使って値を格納する方法です。下記の例を見てください。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
class Television{
int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
上記の場合、作成されたオブジェクトを使ってクラスの中に定義されたメソッドを呼び出しています。そのメソッドの中で、メンバ変数に値を格納しています。「オブジェクト変数.メンバ変数=値」の記述方法でメンバ変数に値を格納できるのに、何故このような一度メソッドを呼び出す手間をかける場合があるのかについては次のページで見ていきます。 |
|
前のページで、メンバ変数に直接値を格納する代わりに、いったんクラスの中で定義しているメソッドを呼び出してメンバ変数に値を格納する方法も記述しました。では何故このようなことをするかと言いますと、例えばメンバ変数に格納できる値の事前チェックをしたり、メンバ変数と他のメンバ変数が連動している場合などの処理を行うことが出来るからです。 例えばテレビのチャンネルは「1」か「3」しか実際には存在しないとします。その場合にメンバ変数に直接値を格納すると、下記のように実際には存在しない値でも格納することが出来てしまいます。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.channelNo = 5;
}
}
class Television{
int channelNo;
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
これを防止する代わりにいったんメソッドを呼び出し、その中で格納できる値かどうかを事前にチェックすることが出来ます。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChanne(5);
}
}
class Television{
int channelNo;
void setChannel(int newChannelNo){
if ((newChannelNo == 1) || (newChannel == 3)){
channelNo = newChannelNo;
}
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
大規模なシステムになってくると、各クラスを別々の人が作成するということもあります。その場合、メンバ変数に直接値を記述できるような形にしていると、クラスを設計した人が予期しない値を直接格納する危険性もあります。その為、いったんメソッドを呼び出し、その中でメンバ変数に値を格納するということはよく行います。また別の例として、メンバ変数を変更した時に他のメンバ変数も連動して変更しておきたい場合を考えてみます。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChanne(5);
}
}
class Television{
int channelNo;
String housouKyoku;
void setChannel(int newChannelNo){
if (newChannelNo == 1){
channelNo = 1;
housouKyoku = "FujiTV";
} else if (newChannelNo == 3){
channelNo = 3;
housouKyoku = "NHK";
}
}
void setHousoukyoku(String newHousouKyoku){
if (newHousouKyoku.equals("FujiTV")){
channelNo = 1;
tvKyoku = "FujiTV";
} else if (newHousouKyoku.equals("NHK")){
channelNo = 3;
tvKyoku = "NHK";
}
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
上記の例ですと、チャンネル番号を変更すると、自動的に他のメンバ変数で放送局を表す「housouKyoku」の値も変更されています。また逆に放送局の値を変更するとチャンネル番号も変更されます。このようにメンバ変数の値を変更することで他のメンバ変数の値も同期して変更したい場合などにメソッドを呼び出す形にしていることで可能になります。もちろん「tv1.channelNo = 1;」とした後で「tv1.housouKyoku = "FujiTV";」という命令を同時に行っておけば同様のことが行えますが、複雑に連動している場合にはいちいち記述するのも面倒ですし、記述するのを忘れてしまったり、間違えて記述してしまったりといった不具合を未然に防ぐ事も出来ます。 どんな場合でもこのようにした方がいいわけでは無いので、目的に応じて直接メンバ変数に値を格納できるようにしたり、メソッド経由で行うようにしたりといった判断を行って下さい。 |
|
前のページでメンバ変数に値を格納する場合にメソッドを呼び出して行う方法について記述しましたが、今のままではメンバ変数を変更するためのメソッドを用意したとしても、直接メンバ変数に値を格納しようと思えば出来てしまいます。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChanne(5);
}
}
class Television{
int channelNo;
void setChannel(int newChannelNo){
if ((newChannelNo == 1) || (newChannel == 3)){
channelNo = newChannelNo;
}
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
では、メンバ変数に直接値を格納するのを禁止し、必ずメソッド経由でしかメンバ変数の値を格納したり参照したり出来ないようにするにはどうすればいいでしょうか。そういった場合はメンバ変数の定義をする時に先頭に「private」を付けるようにします。
class Television{
private int channelNo;
void setChannel(int newChannelNo){
if ((newChannelNo == 1) || (newChannel == 3)){
channelNo = newChannelNo;
}
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
「private」を頭に付けたメンバ変数は、メンバ変数が定義されているクラスの中でしか値を格納したり取り出したりということが出来なくなります。その為、他のクラスの中でオブジェクトを作成し「オブジェクト名.メンバ変数名 = 値」のように直接メンバ変数に値を格納することを防止できます。「private」を付けた場合でも、同じクラス内のメソッドからは値を格納したり取り出したりを行えますので、他のクラスの中でオブジェクトを作成した場合でも、オブジェクトからはメソッドを呼び出し、メンバ変数の値の変更はそのメソッド内で行うように記述すれば、メソッドを呼び出すことで間接的にメンバ変数の格納や変更を行えます。つまりメンバ変数の直接の変更は禁止し、メソッド経由でのみメンバ変数の変更を許可出来るというわけです。 定義時にprivateを頭に付けたメンバ変数は、メンバ変数を定義したクラス内でし かメンバ変数の値を変更したり参照したりする事は出来ない。 private 変数の型 メンバ変数名;実際に一度試してみましょう。「private」を付けたメンバ変数を直接変更してみます。 サンプルプログラム下記のサンプルを実行してみよう。
class ctest2{
public static void main(String args[]){
Television tv1 = new Television();
tv1.channelNo = 1;
}
}
class Television{
private int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
System.out.println("新しいChannelNo=" + channelNo);
}
}
上記を実行しようとすると、コンパイルの段階でエラーとなります。
[xxxxxxxx@dddddddddd Member]$ javac ctest2.java
ctest2.java:5: エラー: channelNoはTelevisionでprivateアクセスされます
tv1.channelNo = 1;
^
エラー1個
[xxxxxxxx@dddddddddd Member]$
これに対してメソッド経由で変更しようとした場合は、メソッドはメンバ変数と同じクラス内なので問題なく変更することが出来ます。サンプルプログラム下記のサンプルを実行してみよう。
class ctest3{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
class Television{
private int channelNo;
void setChannel(int newChannelNo){
channelNo = newChannelNo;
System.out.println("新しいChannelNo=" + channelNo);
}
}
上記をコンパイルした後で実行すると次のように表示されます。[xxxxxxxx@dddddddddd Member]$ java ctest3 新しいChannelNo=1 [xxxxxxxx@dddddddddd Member]$では、「private」を付けていないメンバ変数はどういう扱いになっているかと言うと、何も付いていない場合は「public」が付いているのと同等になります。つまり今までは「public」と書くのを省略してわけです。 「public」が付いている場合は、同じクラス内はもちろん、クラスの外部からでもメンバ変数に値を格納したり参照したりと言ったことが可能です。 定義時にpublicを頭に付けたメンバ変数は、メンバ変数を定義したクラス外か らでもメンバ変数の値を変更したり参照したりする事が出来る。 public 変数の型 メンバ変数名; なお、publicは省略することが出来るので、単に下記のように記述も出来る。 変数の型 メンバ変数名;publicは書いても書かなくてもいいのですが、後でプログラムを見て分かりやすいように、そして「private」を書き忘れたわけではなく「public」にしたかったのだと明示的に分かるようにするために、publicは書いておいたほうがいいかと思います。 |
|
ここでは再度メンバメソッドについて見ておきます。メンバメソッドとはクラスの中で定義されているメソッドのことで、そのクラスの中で何かの動作をするためのものです。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
class Television{
int channelNo;
void setChannel(int newChannelNo){
if ((newChannelNo == 1) || (newChannel == 3)){
channelNo = newChannelNo;
}
}
void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
メンバメソッドはクラス内で使われると言うだけで、Javaプログラム入門の中で記述したメソッドを使うで記述したことと変わりはありません。メソッドを呼び出すときに引数を指定したり、メソッドからの戻り値を設定することもできます。メンバメソッドの呼び出し方は、メンバ引数の場合と同様で、同じクラス内から呼び出す場合には「メンバメソッド名(引数)」か「this.メンバメソッド名(引数)」と記述しますし、メンバメソッドが定義されているクラスの外からオブジェクトを介して呼び出す場合には「オブジェクト名.メンバメソッド名(引数)」の形式で呼び出します。 publicとprivateまたメンバ変数と同じように、メンバメソッドにも「private」と「public」を付けることが出来ます。省略した場合は「public」が記述されている場合と同等です。例えば下記のようになります。
class Television{
int channelNo;
public void setChannel(int newChannelNo){
channelNo = newChannelNo;
}
private void dispChannel(){
System.out.println("現在のチャンネルは " + channelNo + " です");
}
}
メンバメソッドをオブジェクトを介して外部から呼び出すには、必ず「public」と付けるか省略しておく必要があります。こちらも一度試しておきましょう。
class ctest4{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
class Television{
private int channelNo;
private void setChannel(int newChannelNo){
channelNo = newChannelNo;
System.out.println("新しいChannelNo=" + channelNo);
}
}
上記を実行しようとすると、コンパイルの段階でエラーとなります。
[xxxxxxxx@dddddddddd Member]$ javac ctest4.java
ctest4.java:5: エラー: setChannel(int)はTelevisionでprivateアクセスされます
tv1.setChannel(1);
^
エラー1個
[xxxxxxxx@dddddddddd Member]$
今度はメンバメソッドに「public」を付けて試してみましょう。
class ctest5{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
class Television{
private int channelNo;
public void setChannel(int newChannelNo){
channelNo = newChannelNo;
System.out.println("新しいChannelNo=" + channelNo);
}
}
実行した結果は下記のようになります。[xxxxxxxx@dddddddddd Member]$ javac ctest5.java [xxxxxxxx@dddddddddd Member]$ java ctest5 新しいChannelNo=1 [xxxxxxxx@dddddddddd Member]$今度は「public」が付いていますのでオブジェクト経由でメンバメソッドが定義されているクラスの外からメソッドを呼び出すことが出来ています。 mainメソッドのpublic今までずっと使っていてmainメソッドを改めて見て下さい。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
}
}
今までmainメソッドの先頭についている「public」について説明を行ってきませんでしたが、この「public」は本ページで説明した通り、mainメソッドが外部から呼びだし可能であるために記述しています。mainメソッドは、プログラムの開始時に最初に呼び出されて実行されるメソッドですので、どこか他のクラスの中から呼ばれる事は無いのですが、Javaのシステムから呼び出しをされますので外部からアクセス可能なように「public」を必ず付けておく必要があります。 |
|
メソッド名は、同じクラス内で既に定義されている他のメソッド名と同じ名前を付けることは出来ませんが、引数が異なる場合は同じ名前のメソッドを同じクラス内で定義することが出来ます。引数が異なるとは下記のようなことです。
class Television{
private int channelNo;
private String housouKyoku;
public void setChannel(int newChannelNo){
channelNo = newChannelNo;
if (newChannelNo == 1){
housouKyoku = "FujiTV";
}else if(newChannelNo == 3){
housouKyoku = "NHK";
}
}
public void setChannel(String newHousouKyoku){
housouKyoku = newHousouKyoku;
if (newHousouKyoku.equals("FujiTV")){
newChannelNo = 1;
}else if(newHousouKyoku.equals("NHK")){
newChannelNo = 3;
}
}
}
上記で「setChannel」メソッドは3つ定義されていますが、メソッドが呼び出された時に渡されてくる引数が最初のメソッドでは「int型」の値であるのに対して、2番目のメソッドでは「String型」です。このようにメソッド名は同じでも引数が異なる場合は同じメソッド名でメソッドを定義することができます。このようにした場合、呼び出す時の引数の型によって、どちらのメソッドが呼ばれるかが決まります。
class ctest{
public static void main(String args[]){
Television tv1 = new Television();
/* 引数がint型なので最初のメソッドが呼び出されます */
tv1.setChannel(1);
/* 引数がString型なので2番目のメソッドが呼び出されます */
tv1.setChannel("NHK");
}
}
class Television{
private int channelNo;
private String housouKyoku;
public void setChannel(int newChannelNo){
channelNo = newChannelNo;
if (newChannelNo == 1){
housouKyoku = "FujiTV";
}else if(newChannelNo == 3){
housouKyoku = "NHK";
}
}
引数の型だけでなく、引数の個数なども含めて、引数の部分が異なっていれば同じ名前のメソッドが定義出来ます。注意頂きたい点としては、戻り値の型が異なるだけでは同じ名前のメソッドを定義することは出来ません。例えば下記のような記述は行えません。
class Television{
private int channelNo;
private String housouKyoku;
public int setChannel(int newChannelNo){
channelNo = newChannelNo;
if (newChannelNo == 1){
housouKyoku = "FujiTV";
}else if(newChannelNo == 3){
housouKyoku = "NHK";
}
return channelNo;
}
public String setChannel(int newChannelNo){
channelNo = newChannelNo;
if (newChannelNo == 1){
housouKyoku = "FujiTV";
}else if(newChannelNo == 3){
housouKyoku = "NHK";
}
return housouKyoku;
}
}
上記の場合は、戻り値の型は異なっていますが、引数の部分がまったく同じなので上記のような使い方は出来ません。では一度サンプルで試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。
class ctest6{
public static void main(String args[]){
Television tv1 = new Television();
tv1.setChannel(1);
tv1.setChannel("NHK");
tv1.setChannel(3, "NHK総合");
}
}
class Television{
private int channelNo;
private String housouKyoku;
public void setChannel(int newChannelNo){
channelNo = newChannelNo;
if (newChannelNo == 1){
housouKyoku = "FujiTV";
}else if(newChannelNo == 3){
housouKyoku = "NHK";
}
System.out.println("チャンネルが変更されました");
dispChannelInfo();
}
public void setChannel(String newHousouKyoku){
housouKyoku = newHousouKyoku;
if (newHousouKyoku.equals("FujiTV")){
channelNo = 1;
}else if(newHousouKyoku.equals("NHK")){
channelNo = 3;
}
System.out.println("放送局が変更されました");
dispChannelInfo();
}
public void setChannel(int newChannelNo, String newHousouKyoku){
channelNo = newChannelNo;
housouKyoku = newHousouKyoku;
System.out.println("チャンネルと放送局が変更されました");
dispChannelInfo();
}
private void dispChannelInfo(){
System.out.println("チャンネルNoは" + channelNo);
System.out.println("放送局は" + housouKyoku);
}
}
上記をコンパイルした後で実行すると次のように表示されます。[xxxxxxxx@dddddddddd Member]$ java ctest6 チャンネルが変更されました チャンネルNoは1 放送局はFujiTV 放送局が変更されました チャンネルNoは3 放送局はNHK チャンネルと放送局が変更されました チャンネルNoは3 放送局はNHK総合 [xxxxxxxx@dddddddddd Member]$ |
|