Java言語入門 〜C言語を学んだ君へ〜
[1] final
			finalキーワードについての説明をします。
			これを使うことで、バグの少ないソースを書くことができます。
			また、「定数」の作り方についても説明します。
			「final」とは文字通り「最後」という意味で、これ以上変更したくない場合に使用します。
			
finalの書式
				final class A {  // classの前にfinalを付ける
				    :
				}
				
			final void method() {   // 戻り値の前にfinalを付ける
			    :
			}
			
final int VALUE = 10; //型宣言の前にfinalを付ける
先頭にfinalをつけます。finalにつけたものに関して変更が禁止されます。
finalは、クラス、メソッド、メンバ変数それぞれで使うことができます。
クラスのfinalを付けた場合には継承ができません。
メソッドにfinalを付けた場合には、オーバーライドによる書き換えができません
変数のにfinaつけた場合には、C言語の[定数]のような役割を果たします。
#define VALUE 10
これとほぼ同じです。
しかし、Javaの定数は「型」を持ち、「修飾子」を付けられます。
つまり、C言語の定数より機能が上です。
定数名は「大文字で書くことが一般的」であり、これについてはC言語と共通しています。
補足として、上記の書き方でも十分定数の役割を果たしますが、基本的にそのような書き方をしません。
定数は次のように書きます。
static final int VALUE = 10;    // 基本的な定数の宣言方法
サンプルプログラム
public class Java09_02 {
	    public static void main(String args[]) {
		        FinalClass2 fc2 = new FinalClass2();
		        //fc2.a = 20; final変数は値の変更ができない
		        fc2.func();
	    }
}
class FinalClass{
	    //final定数を宣言
	    final public int a = 10;
	
	    //finalメソッドを定義
	    final public void func(){
		        System.out.println("funcメソッド");
	    }
}
class FinalClass2 extends FinalClass{
	    //final public void func(){} finalメソッドはオーバーライドできない
}
実行結果
			
			finalを使ったプログラムを書いてみました。
			コメント部分の//を消してみればわかると思いますが、変更ができません。
			このようにこれ以上変更する予定がない場合などに用いられます。
			
			
[2] 継承とアクセス修飾子
継承関係におけるアクセス修飾子の効果について説明します。
「パッケージ」を学習していない段階では、アクセス修飾子をすべて理解できません。
ここでは、アクセス修飾子について少しだけ説明します。
「パッケージ」については、第11回パッケージを見てください。
			
アクセス修飾子の復習
第8回で説明したように、アクセス制御には以下の方法がありました。
| アクセス修飾子 | 制御の強さ | 機能 | 
|---|---|---|
| public | 弱い | 全てのクラスからアクセスを許可 | 
| protected | 少し弱い | 同じパッケージ、継承先からのアクセスを許可 | 
| private | 強い | 同じのクラスからのアクセスを許可 | 
| なし | 少し強い | 同じパッケージからのアクセスを許可 | 
次の項目で、継承とこれら4種類のアクセス制御について説明します。
アクセス修飾子の効果
継承の場合のアクセス修飾子の効果を、メンバ変数を例に説明します。
次のソースを見てください。
アクセス修飾子のプログラム
class Super {
    public int a;
    protected int b;
    private int c;
    int d;
    public super() {
        a = b = c = d = 10;
    }
}
class Sub extends Super {
    public Sub() {
        super();
        System.out.println("a="+a);
        System.out.println("b="+b);
        //System.out.println("c="+c);        ここだけコンパイルエラー
        System.out.println("d="+d);
    }
}
public class Java09_03{
    public static void main(String args[]){
        Sub sub = new Sub();
    }
}
実行結果
			
スーパークラスSuperに4種類のアクセス制御を持たせたint型を定義しています。
サブクラスSubで各int型のメンバ変数にアクセスを試みます。
結果、「private」のみアクセスができません。
アクセス制御方法の一覧と比較して、理解ができたと思います。
この例では、「public」はすべてのクラスからアクセスできるので良いとしても、
「protected」と「アクセス修飾子なし」の違いがわかりにくいです。
その違いは、第11回「パッケージ」で説明します。
今は、「protected」は継承した先からアクセスできると覚えておいてください。
[3] オブジェクトのキャスト
オブジェクトのキャストについて説明します。
基本データ型のキャストとの違いを学んでください。
			
基本データ型の違い
キャストするには基本データ型のキャストは次のように行いました。
double value1 = 1.414;
int value2 = (int)value1;    // int型にキャスト
double value3 = value2;    // キャストしてdouble型に戻す
大きいデータ型から小さいデータ型にキャストする場合は「(データ型)」を使っています。
次にオブジェクトをキャストする方法を説明します。
B b1 = new B();
A a = b1;        // A型にキャスト
B b2 = (B)a;     // キャストしてB型に戻す
AとBはオブジェクトです。
B型のオブジェクトをA型にキャストし、それをB型に戻しています。
ここで注意することは、AはBのスーパークラスです。
この継承関係が成り立つ時のみ、キャストが行えます。
例えば、次のクラス関係があったとします。

この場合、
・BはAにキャストできる
・CはBにキャストできる
・CはAにキャストできる
以上の3パターンのみキャストできます。
キャストした場合のオブジェクトと基本データ型の違いを説明します。
まず、基本データのキャストについてです。次のソースを見てください。
キャストのサンプルプログラム
public class Java09_04 {
    public static void main(String args[]) {
        double value1 = 1.414;
        int value2 = (int)value1;
        System.out.println(value2);        // int型にしたデータを出力
        System.out.println((double)value2);        //double型に戻して出力
    }
}
実行結果

一度キャストしたデータを元の型に戻しても、失ったデータは戻りません。
当然といえば当然です。次にオブジェクトのキャストです。
次のソースを見てください。
オブジェクトのサンプルプログラム
class A {
    int a;
    public A() {
        a = 10;
    }
}
class B extends A {
    int b;
    public B() {
        super();
        b = 20;
    }
}
public class Java09_05 {
    public static void main(String args[]) {
        B b = new B();
        A a = b;
        System.out.println(a.a);
        //System.out.println(a.b); クラスBの変数は使えない
        B b1 = (B)a;
        System.out.println(b1.b);        // クラスBの変数が使える
    }
}
実行結果

ソース中、コメントアウトした処理があります。
それは「System.out.println(a.b);」で、当然エラーになる処理です。
理由は、クラスAにキャストしたために、クラスBの機能が使えなくなったからです。
ない機能を使えるはずがありません。しかし、一度クラスAにキャストしたオブジェクトを、
元のクラスBにキャストすると、クラスBの機能が使えるようになります。
オブジェクトのキャストでは、基本データ型のキャストと異なり、機能(データ)がなくなりません。
キャストをしても、メモリ上にはクラスBの情報が残ります。
オーバーライドとキャスト
キャストをしてもデータは消えないと説明しました。
しかし、キャストしたクラスにない機能は使用できませんでした。
では、キャスト先とキャスト元の両方にある機能を見てみましょう。
つまり、「オーバーライドしたメソッド」はどのような結果になるでしょう。
次のソースを見てください。
オーバーライドしたメソッド
class C {
    public void print() {
        System.out.println("クラスCです");
    }
}
class D extends C {
    public void print() {
        System.out.println("クラスDです");
    }
}
public class Java09_06 {
    public static void main(String args[]) {
        D d1 = new D();
        C c = d1;
        c.print();    // キャストしたあとメソッドの呼び出し
        D d2 = (D)c;
        d2.print();    // 元の型にキャストしてメソッドの呼び出し
    }
}
実行結果
				
				Dクラスのオブジェクトを、Cクラスにキャストした時とDクラスに戻した時
				それぞれで、メソッドを呼び出しています。
				
				クラスCです
				クラスDです
				
				と表示されるように思えますが、それは誤りです。
				実際はキャストしていても、元のクラスのメソッドが呼び出されます。
				少しわかりにくいですが、この点に注意してください。
				しかし、これを利用すると、「ポリモーフィズム」を実現する大きな利点になります。
				それについては、次のページで説明します。
				
 
				 
					 
				 
				