インターフェースとは
プログラムを作成するときに、決められた操作を行うためのメソッドがまとめて定義されているクラスのようなもの。そのクラスで使うメソッドをインターフェースで定義しておく。インターフェースではメソッドの中身は空にしておいて、インターフェースを使うクラス内で中身を実装することが多い。
//インターフェース
interface インターフェース名{
//メソッド定義
void method A(){}
void method B(){}
}
//インターフェースをクラスに実装
class A implements インターフェース名 {
public void methodA {
//実装する
}
}
使用場面
クラス間の関係性を定義する場合: インタフェースは、複数のクラスが共通のメソッドや振る舞いを持つ場合に使用される。例えば、異なるクラスが同じインタフェースを実装することで、それらのクラスが共通の操作を提供することができる。これにより、クラス間の関係性を明確に定義し、柔軟性と拡張性を向上させることができる。
多態性を実現する場合: インタフェースはポリモーフィズムを実現するために使用される。異なるクラスが同じインタフェースを実装することで、それらのクラスのインスタンスを統一的に扱うことができる。プログラム内でインタフェース型を使用することで、実行時に適切なクラスのメソッドが呼び出されるため、柔軟なオブジェクトの操作が可能となりる。
コードの再利用性を高める場合: インタフェースは、複数のクラスで共通のメソッドを定義することにより、コードの再利用性を高める。インタフェースを実装することで、複数のクラスで同じインタフェースメソッドを再利用することができる。また、新しいクラスを追加する際に、既存のインタフェースを実装するだけで、既存のコードとの互換性を保つことができる。
インターフェースの役割
1. ポリモーフィズムを実現する
オブジェクト指向におけるポリモーフィズム(多様性)を実現する。
ポリモーフィズムとは、複数のオブジェクトが同じ命令に対して違う挙動を可能にすること。
例えば猫、犬、鳥のオブジェクトがあった時、どれも鳴くという共通の命令はあるが、それぞれ「にゃー」「わん」「ピヨ」と挙動は違う。
この鳴くという共通の動作の入り口を定義することがインターフェースの役割。
interface Animal {
void cry();
}
//同じcryメソッドを使用しているが、出力される結果はそれぞれ違う
class Cat implements Animal {
public void cry() {
System.out.println("にゃー");
}
}
class Dog implements Animal {
public void cry() {
System.out.println("わん");
}
}
class Bird implements Animal {
public void cry() {
System.out.println("ピヨ");
}
}
2.実装漏れを防ぐ
インターフェースで定義したメソッドは必ず、実装クラスでオーバーライドしなければならない。
オーバーライドしなかった場合はコンパイルエラーが出るので実装漏れを防ぐことができる。
3.柔軟性と拡張性をあげる
public interface Calculation {
int sum(int num);
}
public class A implements Calculation {
@Override
public int sum(int num) {
int price = num + 100;
return price;
}
}
public class B implements Calculation {
@Override
public int sum(int num) {
int price = num + 200;
return price;
}
}
Calculationインタフェースをパラメータとして受け取っている。このように、メソッドの引数や戻り値としてインタフェースを使用すると、Calculationインタフェースを実装したクラスなら全て受け取れる柔軟性が生まれる。
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
//同じ記述で呼び出せる
settlement(a);
settlement(b);
}
public static void settlement(Calculation calculation) {
int num = calculation.sum(100);
System.out.println("計算結果:" + num);
}
}
defaultメソッド
予め中身の処理が書かれているメソッドをインターフェースで実装することができるメソッド。
メソッドの型の宣言の前にdefault句を記述する。
interface Animal {
default void sleep() {
Sysyem.out.println("寝てます");
}
}
class Cat implements Animal {
@Ovierride
public void sleep() {
Animal.super.sleep(); //継承元のデフォルトメソッドの呼び出し
}
}
ネストしたインターフェース
インターフェースの中にクラスを宣言することも可能。
インターフェース内のクラスは常にpublicかstatic。慣習的にpublicとstaticは省略する。
interface Animal {
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Cat getCat();
}
class MyImpl implements Animal {
@Ovierride
public Cat getCat() {
return new Animal.Cat("猫");
}
}
Animal animal = new animal();
Animal.Cat cat = animal.getCat();
System.out.println(cat.getCat());
多重継承
同じシグネチャのメソッドを持つ2つのインターフェースを多重継承したクラスは、そのメソッドの実装を1つ持てば良い。
interface A {
void method();
}
interface B {
void method();
}
class C implements A,B {
//methodメソッドは1つで良い
@Ovierride
public void method() {
System.out.println("メソッド");
}
}
異なるシグネチャのメソッドを持つ2つのインターフェースを多重継承した場合は、それぞれのメソッドを実装する。
interface A {
void methodA();
}
interface B {
void methodB();
}
class C implements A,B {
@Ovierride
public void methodA() {
System.out.println("メソッドA");
}
@Ovierride
public void methodB() {
System.out.println("メソッドB");
}
}
多重継承した2つのインターフェースが継承関係のある型の返り値を持つ同名のメソッドを持っていた場合は、継承関係で下の方に位置する型のメソッドを実装すれば良い
interface A {
Object methodA();
}
interface B {
String methodA();
}
class C implements A,B {
//Object→StringなのでStringの方を実装する
@Ovierride
public String methodA() {
System.out.println("メソッドA");
}
}