1.抽象クラスと抽象メソッド
・抽象メソッドとは処理のないメソッドのこと。
(処理のあるメソッドは具象メソッドと呼ばれる。)
・抽象クラスとは抽象メソッドを持つことができるクラス
(具象メソッドを混在可。)
・なぜ抽象化が必要なの??
実装(抽象メソッドでオーバーライド)する時に、メソッド名、引数リスト、戻り値などがサブ側でミスするとコンパイルエラーになるので、型の統一がし易い。
2.インターフェース
インターフェースとは…
「クラスの仕様」であり、「ただの取り決め」である。
[修飾子] interface インターフェース名{ }
[修飾子] interface インターフェース名 extends インターフェース名{ }
※インターフェース同士の継承ではextendsを使用します。
※{}内は定数、抽象メソッドのみ!
<インターフェースの特徴>
●インタフェース宣言には、interfaceキーワードを指定する
●インスタンス化できない為、使用する場合は実装クラスを定義する
●実装クラスを定義する場合はimplementsキーワードを使用する
public class 実装クラス名 implements インターフェース名 {}ex)
public class Super implements MyIf {}
◆インターフェース内での暗黙的仕様
①インターフェースでの変数とメソッド
インターフェースで宣言、定義できるのは抽象メソッドと定数のみ。という理由から、コンパイル時に勝手なことをします。
<抽象メソッドの場合>
ソースに書いていなくても勝手に
public abstract
をメソッドに付けてしまいます。
その為、修飾子にprotectedを使うとコンパイルエラーになります。
何故コンパイルエラーになるのかわかりますよね??
分からなければ、調べて理由を提出しよう!!
<定数の場合>
ソースに書いていなくても勝手に
public static final
を宣言した変数に付けてしまいます。
その為、変数を初期化していないとコンパイルエラーになります。
こっちも…何故コンパイルエラーになるのかわかりますよね??
分からなければ、調べて理由を提出しよう!!
※課題の提出先は営業さん宛てに、ファイル名は「8章小課題.txt」にしてメール送信しましょう。送ってこないなら…勉強してないということになりますね。
②インターフェースの実装クラス
インターフェースはインスタンス化できない為、メソッドをオーバーライドしたクラスを定義する必要があり、これをインターフェースの実装クラスと呼びます。実装クラスの宣言にはimplementsキーワードを使用します。
[修飾子] class クラス名 implements インターフェース名 { }
実装クラスの宣言の際、2つ特徴があります。
1.多重継承(のようなもの)が可能
ex) [修飾子] class Myclass implements If1, If2 { }
複数の親を子は持てない、がルールでしたよね。つまり「多重継承が出来ない」ということでした。しかし、インターフェースを使えば、擬似的に多重継承が出来るということになります。
2.実装と同時に継承も出来る!
ex) [修飾子] class Myclass extends Super implements If1{ }
※継承の記述が先!
抽象クラスとインターフェース
インターフェースの特徴を説明してきましたが、ここでちょっとした疑問がありませんか?
『インターフェースと抽象クラスってどう使い分けるの?』
上記、実装クラスの特徴にある通り、多重継承が可能という影響しているのだと思うのですが、
継承関係にあって処理の再利用したいなら、抽象クラス
クラス仕様としての型定義したいなら、インタフェース
また、
抽象-派生クラスの関係は、IS A関係と呼ばれており、
IF-実装クラスの関係は、CAN DO関係と呼ばれています。
public abstract class Animal {}
public class Rabbit extends Animal {}うさぎ is 動物
public interface Jumpable {}
public class Rabbit implements Jumpable {}うさぎ can do ジャンプ
どうですか?イメージの一助になればと思います。
3.型変換
(扱える範囲内であれば)型変換でデータの代入を可能にする。
◇暗黙とキャスト
並べてみると・・・
上図のような形でデータ型の型変換が可能です。
以下、例文です。
ex)
int a = ‘A’; //OK
//’A’はchar型なので暗黙でOKint a = (int)3.14 //OK
//aの中身は3
//キャストで整数型になる。
◇参照型の型変換
型変換はデータ型のみではなく、オブジェクト(クラスやインターフェース)も可能です。
データ型同様、変換にはルールがある。
※データ型の[小 → 大] のように、[先 → 元] の関係がある。
例文を書きます。
分かり難いでしょうか。。
ちゃんと整理するためにも、以下の例を確認してみましょう。
ex)
A a = new A();
X x = (X)a;
コンパイルは通る、実行時エラー
(aをスーパークラスでインスタンス化してるから)ex)
A a = new X();//暗黙の型変換
X x = (X)a;
コンパイルも実行も通る(aをサブクラスでインスタンス化してるから)ex)
X a = new X();//サブクラス型で宣言
A b = a;//aをスーパークラスの型に入れる
X x = (X)b;
コンパイルも実行も通る(aをサブクラスでインスタンス化してるから)
親でインスタンス化したものをキャストして子に入れると、コンパイルは通りますが、ClassCastExceptionで落ちます。
上記の例にある通り【子→親→(キャスト)子】のケースがあるため、親から子へのコンパイルは通すしかない、という理解で良いと思います。
因みに子クラスをインスタンス化して親クラスに暗黙キャストで入れた場合
要は【子→親】のケースですね、このとき子クラスにしかないメソッドは親クラスでは使えません。
その後、親クラスから子クラスへ再度キャストして入れた場合は、
要は【(子→)親→(キャスト)子】ですね、このときは子クラスのメソッドが再度使えるようになります。
ま、参考までに。。
ちょっと話が逸れますが、キャストは無敵ではありません。
継承関係のないクラス同士はキャストしてもコンパイルエラーです。
あくまでも「問題ないケースがあるから通る」ということですね。
もうちょっと例文を見てみましょう。
class Super{
void methodA(){ // ① }
}
class Sub extends Super{
void methodA(){ // ② }
void methodB(){}
}
class Test{
Super S1 = new Sub();
S1.methodA(){ } ; // ②が実行、オーバーライド
S1.methodB(){ } ; // コンパイルエラー
// SuperにmethodBはないため
}
methodAについてはオーバーライドしているSubクラスを見ているのに、methodBについては、Superにないからコンパイルエラー。
違和感がありますが、こういう挙動をします。
◇ポリモフィズム
第5章でポリモフィズムの概念は説明しましたが、その実現のために抽象クラスや具象クラス、インターフェースや実装クラスを活用しています。
これにより、同じ名前のメソッドの呼び出しであっても、オブジェクトごとに異なる機能を提供することを実現しています。
4.パッケージ
これまでは、クラスをそのまま記述したものでした。
実際にそうやると名称が被ってしまったり、使えなかったりするので、階層を設けてパッケージ化します。
◇import文
違うパッケージのクラスを指定するには・・・
1,完全な形でクラスを指定する
ex)
com.se.Foo f = new com.se.Foo();
2,import文を使う(ソースの先頭)
ex)
import com.se.Foo ; //クラスを指定
import com.se.* ; //se配下の全てのクラス
//import com.* ; //パッケージの途中で*はエラー
※パッケージをimportはpackage文が先に記述。
コラム〜java.langパッケージについて〜
JavaにはAPIというJavaが提供するクラス群がある。
それらのクラスの中でも特に利用度の高いものはjava.langパッケージ
に属しており、コンパイル時に自動でimportされる。
java.langパッケージにはObject、System、Stringクラスなどが含まれている。
そのため、自身でObjectクラスを作成していない状態で、
public class クラス名 extends Object{}
という記述をしてもコンパイルエラーにならない。また、
interface Object{}
class Object{}
といった記述もコンパイルエラーにならない点も注意が必要。