クラスの継承
インターフェイス
インターフェイスは、クラスの仲間
interface
implements
目次
1. インターフェイスとは
↓↓↓ 実装 ↓↓↓
それじゃ。
少しずつ解説するぞ。
2. インターフェースの宣言
interface
interfaceキーワードを使って宣言します。
あっ!
「class」じゃない!
インターフェースは、
「すべてのインスタンスメソッドが抽象メソッドである抽象クラス」とは違います。
そもそも、abstract class ではなく、
interface で宣言します。
でも、ソースファイルを保存するときの拡張子は普通に「.java」です。
なぜ悪役?
ふっふっふ。
驚いたか?
3. インターフェースの書き方
インターフェースに定義できることはとても限られています。
初めのうちは
「抽象メソッドのみ」
くらいに考えていいと思う。
インターフェースの特徴
- インターフェースのメンバは、すべて暗黙的に public が付与されます。
- インスタンスメソッドは、すべて暗黙的に public abstract が付与されます。
(default による例外があります。)
- 変数には、すべて暗黙的に public static final が付与されます。
(インスタンス変数は持てません。)
- クラスメソッド(static)は処理を実装できます。
「暗黙的に」ってことは
書かなくていいってこと?
Yes.
暗黙的に付与される修飾子まとめ
| メンバ | 暗黙的に付与される修飾子 |
| 変数 | public static final |
| クラスメソッド(static) | public |
| インスタンスメソッド | public abstract |
すべてのインスタンスメソッドが
抽象メソッドなので、
abstract も書かなくていいぞ。
重要
すべてのインスタンスメソッドが抽象メソッドです。
4. インターフェースの実装
implements
インターフェースの実装には「implements」 を使用します。
ここでちょっとポイント。
インターフェースの場合
「継承」ではなく「実装」という。
なんでじゃ?
インターフェースの
抽象メソッドを
実装する行為そのもの
だからだろう。
↓↓↓ 実装 ↓↓↓
5. 複数のインターフェースを実装する
implements インターフェース1, インターフェース2 {
重要
複数のインターフェースを実装する場合は、「,」(カンマ)で区切ります。
インターフェイスは
「クラスの多重継承問題」に対する
一つの答えだ。
インスタンス変数を排除した上で
メソッドの実装を継承後に行うことで、
安全な多重継承を実現したと?
そうゆう感じだ。
複数のインターフェースを実装する場合は、「,」(カンマ)で区切ります。
紙面の都合で改行しているが、
普通にカンマ区切りで大丈夫だ。
6. サンプル1 インターフェースの実装
ISpeak がインターフェースです。
「class」ではなく、「interface」で宣言します。
インスタンスメソッドはすべて暗黙的に public abstract が付与されますので、
記述しなくても OKです。
//ISpeak.java
public interface ISpeak {
void printName();
void sayWish();
}
ISpeakを実装した2つのクラス(Dog_91、Cat_91)を作っています。
//Dog_91.java
public class Dog_91 implements ISpeak {
protected String name1;
public Dog_91(String name){
this.name1 = name;
}
@Override
public void printName(){
System.out.println(name1 + "だワン!");
}
@Override
public void sayWish(){
System.out.println(name1 + "は棒には当たらないワン!");
}
}
//Cat_91.java
public class Cat_91 implements ISpeak {
protected String name2;
public Cat_91(String name){
this.name2 = name;
}
@Override
public void printName(){
System.out.println(name2 + "なのにゃ!");
}
@Override
public void sayWish(){
System.out.println(name2 + "は小判が欲しいのにゃ!");
}
}
ちょっと待って!
なんで Dog_91クラスは、name1 で、
Cat_91クラスは、name2 なの?
抽象クラスと
インターフェースとの違いを
強調したくて。
インターフェースはインスタンス変数を持てません。
このため、同じような使い方をする変数であっても、
それぞれのサブクラスで個別に宣言する必要があります。
「継承された変数」ではないことにご注目ください。
配列がインターフェースの
ISpeak型だぞ。
//Driver_91.java
public class Driver_91{
public static void main(String[] args){
ISpeak[] animalArray = new ISpeak[6];
animalArray[0] = new Dog_91("ヨーゼフ");
animalArray[1] = new Dog_91("パトラッシュ");
animalArray[2] = new Dog_91("白いお父さん");
animalArray[3] = new Cat_91("ニャース");
animalArray[4] = new Cat_91("ドラえもん");
animalArray[5] = new Cat_91("ニャンパス");
for(int i=0; i<animalArray.length; i++){
animalArray[i].printName();
animalArray[i].sayWish();
}
}
}
インターフェースの
変数も作れるのね。
ISpeak を実装した
クラスだけが
代入できる。
ISpeak.java
Dog_91.java
Cat_91.java
Driver_91.java
を wsフォルダに保存して実行します。
コマンドライン
>cd ws
ws>javac -encoding UTF-8 Driver_91.java
ws>java Driver_91
ヨーゼフだワン!
ヨーゼフは棒には当たらないワン!
パトラッシュだワン!
パトラッシュは棒には当たらないワン!
白いお父さんだワン!
白いお父さんは棒には当たらないワン!
ニャースなのにゃ!
ニャースは小判が欲しいのにゃ!
ドラえもんなのにゃ!
ドラえもんは小判が欲しいのにゃ!
ニャンパスなのにゃ!
ニャンパスは小判が欲しいのにゃ!
Dog_91も、Cat_91も、ISpeakを実装しているので、
ISpeak型の変数に代入できました。
ISpeakの、printNameメソッドと、sayWishメソッドを実行できます。
7. サンプル2 extendsとの併用
implements は、extends と併用できます。
Animal_09クラスでは、インスタンス変数「name」と、nameを初期化するコンストラクタを宣言しています。
インスタンス変数周りはクラスで宣言して、インスタンスメソッドの定義は、インターフェースで行う作戦です。
共通のインスタンス変数を持つ
サブクラスを作る場合は、
クラスの継承を併用するのもありだ。
//Animal_09.java
public class Animal_09 {
protected String name; //ここで変数を宣言
public Animal_09(String name){
this.name = name;
}
}
Animal_09 と ISpeak を実装した2つのクラス(Dog_92、Cat_92)を
作っています。
変数「name」を Animal_09から継承しています。
//Dog_92.java
public class Dog_92 extends Animal_09 implements ISpeak {
public Dog_92(String name){
super(name);
}
@Override
public void printName(){
System.out.println(name + "だワン!");
}
@Override
public void sayWish(){
System.out.println(name + "は棒には当たらないワン!");
}
}
extends Animal_09 と
implements ISpeak
の間は半角空白文字ね。
//Cat_92.java
public class Cat_92 extends Animal_09 implements ISpeak {
public Cat_92(String name){
super(name);
}
@Override
public void printName(){
System.out.println(name + "なのにゃ!");
}
@Override
public void sayWish(){
System.out.println(name + "は小判が欲しいのにゃ!");
}
}
//Driver_92.java
public class Driver_92{
public static void main(String[] args){
ISpeak[] animalArray = new ISpeak[6];
animalArray[0] = new Dog_92("ヨーゼフ");
animalArray[1] = new Dog_92("パトラッシュ");
animalArray[2] = new Dog_92("白いお父さん");
animalArray[3] = new Cat_92("ニャース");
animalArray[4] = new Cat_92("ドラえもん");
animalArray[5] = new Cat_92("ニャンパス");
for(int i=0; i<animalArray.length; i++){
animalArray[i].printName();
animalArray[i].sayWish();
}
}
}
ISpeak.java
Animal_09.java
Dog_92.java
Cat_92.java
Driver_92.java
を wsフォルダに保存して実行します。
コマンドライン
>cd ws
ws>javac -encoding UTF-8 Driver_92.java
ws>java Driver_92
ヨーゼフだワン!
ヨーゼフは棒には当たらないワン!
パトラッシュだワン!
パトラッシュは棒には当たらないワン!
白いお父さんだワン!
白いお父さんは棒には当たらないワン!
ニャースなのにゃ!
ニャースは小判が欲しいのにゃ!
ドラえもんなのにゃ!
ドラえもんは小判が欲しいのにゃ!
ニャンパスなのにゃ!
ニャンパスは小判が欲しいのにゃ!
ファイル数が
増えましたね~。
そうだな。
このように機能別にファイルを分けて
継承や実装を利用することで
拡張性やメンテナンス性を向上している。
実感できるのはもうちょっと先かなぁ。
8. サンプル3 インターフェースのクラスメソッド
おまけです。
インターフェースのクラスメソッド(staticメソッド)は、インターフェースで実装可能です。
クラスと同様に使用できます。
//ISpeakSt.java
public interface ISpeakSt {
String group = "生き物です。"; //public static final が付与
static void printGroup(){ //public が付与
System.out.println(group);
}
void printName();
void sayWish();
}
//Driver_93.java
public class Driver_93{
public static void main(String[] args){
ISpeakSt.printGroup(); //クラスメソッドの実行
}
}
ISpeakSt.java
Driver_93.java
を wsフォルダに保存して実行します。
コマンドライン
>cd ws
ws>javac -encoding UTF-8 Driver_93.java
ws>java Driver_93
生き物です。
クラスメソッドだから
インスタンス化なしで
そのまま実行できる。
お疲れ様でした。