はじめに
皆さんこんばんは、脆弱性アザラシです。
今回が初投稿となります。
自分は未経験からエンジニアへの転職を目指しており、その過程で行った資格の勉強などの掘り下げを、こちらのブログで発信していきたいと思っています。
初心者ゆえ、至らぬ点などもあるかと思いますが、お手柔らかに、温かい目で見ていただけますと幸いです。よろしくお願いいたします。
今回は、Java SE11 Silver の勉強をしていて、「インタフェースに宣言する抽象メソッドには、処理内容を記述できない」(註:1)といった記述を目にし、「一体どういうことだろう?」と気になったので、その点を掘り下げて考えていきたいと思います。
自分の思考の整理を兼ねているため、より詳細な内容を確認したい方は、(註)に記載している参考文献もご参照ください。
インタフェースの概要
まずは、インタフェースの概要を説明します。
インタフェースとは、「クラスに含まれるメソッドの具体的な処理内容を記述せず、「メソッドの型」「変数」を定義したもの」のことです(註2,3)。
メソッドの具体的な処理内容を記述しなくてよいので、メソッドの型だけを先に記述しておき、後からメソッドを使用する段階で処理内容を記述するという使い方ができます。
これにより、インタフェースを継承することで戻り値が統一でき、クラス間での関係が疎となり、各クラスのメソッドを意識せずとも実装することが可能になります。
プログラミングにおける一般原則として、モジュール間の独立性は高ければ高いほど良い(何か大きな改修をした際に、他のモジュールへの影響が小さいため)です(註4)。
つまり、インタフェースもその原則を守り、コードの利便性を高めるのに、一役果たしているわけですね。
インタフェースの具体例
次に、インタフェースの実際の使い方を見ていきましょう。
interface Animal {
void sound();
}
このAnimalインタフェースは、soundというメソッドを宣言しています。
そして、このsoundメソッドは、voidを返す(つまり何も返さない)メソッドです。しかし、どのような音を出すかはここでは定義されていません。ただ、この後Animalインタフェースを継承した場合には「音を出すべき」という約束ごとだけが定義されています。
次に、このインタフェースを実装するクラスを作ります。
class Dog implements Animal {
public void sound() {
System.out.println("Woof!");
}
}
class Cat implements Animal {
public void sound() {
System.out.println("Meow!");
}
}
上のコードで、DogクラスとCatクラスは、それぞれAnimalインタフェースを実装しています。ここで初めて、soundメソッドがどのような音を出すのかが定義されているということのようです。
インタフェースを実際に使ってみる
最後に、インタフェースをmainメソッドで実際に使ってみます。
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.sound(); // Woof!
myCat.sound(); // Meow!
}
}
mainメソッドでは、Animal型の変数myDogとmyCatをそれぞれDogクラスとCatクラスのオブジェクトに設定しています。
そして、soundメソッドを呼び出すと、それぞれ「Woof!」と「Meow!」が出力されます。
抽象クラスとどう違うのか?
Javaではインタフェースとよく似た概念に、「抽象クラス」と呼ばれる機能があります。メソッドの具体的な処理内容を記述しないことから混同されることが多いですが、この二つは「共通の実装の有無」によって使い分けられます。
先ほどの動物の泣き声の例でもう一度見て見ます。下記はインタフェースの使用例です。
interface Animal {
void eat();
void sleep();
}
class Dog implements Animal {
public void eat() {
System.out.println("Dog is eating.");
}
public void sleep() {
System.out.println("Dog is sleeping.");
}
}
ここでは、具体的な実装に関しては、あくまでもDogクラスに任されているということが読み取れると思います。
これに対して、抽象クラスの使用例は下記のようになります。
abstract class Animal {
abstract void eat();
void sleep() {
System.out.println("Animal is sleeping.");
}
}
class Dog extends Animal {
void eat() {
System.out.println("Dog is eating.");
}
}
こちらの例では、Animalは抽象クラスで、eatメソッドは抽象メソッドとして定義されていますが、sleepメソッドは具体的な処理を持っています。
DogクラスはAnimalクラスを継承し、eatメソッドを具体的に実装しています。
まとめると、もしも複数のクラスに共通のコード(メソッドやフィールド)があり、それを共有したい場合は、抽象クラスが適しているいます。逆に、具体的な処理内容はそれぞれのクラスに依存し、共通の実装が必要ない場合はインタフェースを使うということです。
註
(1)志賀澄人,株式会社ソキウスジャパン『徹底攻略Java SE 11 Silver問題集[1Z0-815]対応』pp245-246,2019年,インプレス.
(2)Qiita「【図解で理解】Javaのinterfaceのメリットと使い方について」@jin007(Nishiyama Ryoh)さん(最終閲覧日:2024年8月30日).
(3)IT業界まるわかりガイド「【Java入門】インターフェースとは?抽象クラス(abstract)との違い」(最終閲覧日:2024年8月30日).
(4)上田勲『プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則』「4.2 結合度」2016年,秀和システム.