Javaにおける「Implements vs extends」の解説
Implementsとextendsの違いと使いどころ
Javaにおけるクラスは、他のクラスから継承することも、インターフェースを実装することもできます。このとき、使用するキーワードはそれぞれ「extends」と「implements」です。
extends
- 継承に使用されます。
- 既存のクラスから新しいクラスを作成し、そのクラスのメソッドやフィールドを継承します。
- 継承関係は親子関係であり、親クラスの特性を子クラスが引き継ぎます。
implements
- インターフェースの実装に使用されます。
- インターフェースで定義された抽象メソッドや定数をすべて実装する必要があります。
- インターフェースは契約であり、その契約を満たすクラスが実装します。
- 複数のインターフェースを実装することも可能です。
具体的な例
// インターフェース
interface Drawable {
void draw();
}
// クラスがインターフェースを実装
class Circle implements Drawable {
public void draw() {
System.out.println("Drawing a circle");
}
}
// クラスが別のクラスを継承
class ColoredCircle extends Circle {
private String color;
// 継承したメソッドをオーバーライド
@Override
public void draw() {
System.out.println("Drawing a " + color + " circle");
}
}
いつ使うか
- extendsを使う場合:
- 既存のクラスをベースに新しいクラスを作成したいとき。
- 親クラスのメソッドやフィールドを再利用したいとき。
- implementsを使う場合:
- ある特定の契約や仕様を満たすクラスを作成したいとき。
- 複数のクラスで共通のメソッドや定数を定義したいとき。
- extendsは継承関係を構築する。
- implementsはインターフェースの契約を実装する。
- 両者は異なる目的を持ち、適切な場面で使用される。
注意
- 一つのクラスは一つのクラスしか継承できないが、複数のインターフェースを実装できる。
- インターフェースは抽象メソッドのみを定義し、具体的な実装は実装クラスで行う。
Javaにおける継承と実装の例
継承(extends)の例
// 親クラス
class Animal {
public void makeSound() {
System.out.println("動物が鳴きました");
}
}
// 子クラスが親クラスを継承
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("ワンワン");
}
}
// 子クラスが親クラスを継承
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("ニャー");
}
}
- Animalクラスは親クラスで、基本的な動物の特性を定義しています。
- DogとCatクラスはAnimalクラスを継承しており、それぞれ犬と猫の具体的な鳴き声を定義しています。
- DogとCatクラスはAnimalクラスのmakeSoundメソッドをオーバーライドして、それぞれの鳴き声を表現しています。
実装(implements)の例
// インターフェース
interface Drawable {
void draw();
}
// クラスがインターフェースを実装
class Circle implements Drawable {
public void draw() {
System.out.println("円を描画します");
}
}
// クラスがインターフェースを実装
class Rectangle implements Drawable {
public void draw() {
System.out.println("長方形を描画します");
}
}
- Drawableインターフェースは、描画に関する契約を定義しています。
- CircleとRectangleクラスはDrawableインターフェースを実装しており、それぞれ円と長方形を描画する具体的な方法を定義しています。
- CircleとRectangleクラスはDrawableインターフェースのdrawメソッドを実装する必要があります。
継承と実装の違い
- 実装は契約であり、インターフェースで定義されたメソッドを実装する必要があります。
- 継承では親クラスのメソッドをオーバーライドできますが、実装ではインターフェースのメソッドを必ず実装する必要があります。
継承の代替手法
- 委譲(Delegation):
- 継承ではなく、オブジェクトをフィールドとして持つことで、他のオブジェクトの機能を利用する手法です。
- 柔軟性が高く、継承による密結合を避けることができます。
class Animal {
public void makeSound() {
System.out.println("動物が鳴きました");
}
}
class Dog {
private Animal animal;
public Dog() {
animal = new Animal();
}
public void makeSound() {
animal.makeSound();
}
}
- ミックスイン(Mixin):
- 複数のクラスから機能を組み合わせる手法です。
- インターフェースや抽象クラスを使用して、複数のクラスから共通の機能を提供します。
interface Flyable {
void fly();
}
class Bird implements Flyable {
public void fly() {
System.out.println("鳥が飛んでいます");
}
}
class Bat implements Flyable {
public void fly() {
System.out.println("コウモリが飛んでいます");
}
}
- 抽象クラス:
- インターフェースと同様に、抽象メソッドやフィールドを持つことができます。
- インターフェースと異なり、具体的な実装を含めることができます。
- 継承関係を構築しながら、共通の機能を提供します。
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
public void draw() {
System.out.println("円を描画します");
}
}
- コンポジション(Composition):
- 継承とは異なり、オブジェクト間の関係はより柔軟です。
class Engine {
public void start() {
System.out.println("エンジンを起動します");
}
}
class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
public void drive() {
engine.start();
System.out.println("車を運転します");
}
}
- 継承は、親子関係を構築し、親クラスの機能を子クラスが継承する際に適しています。
- 実装は、契約を満たすためにクラスがインターフェースのメソッドを実装する際に適しています。
- 委譲やミックスインは、継承による密結合を避け、より柔軟な設計を実現したい場合に適しています。
- 抽象クラスは、継承関係を構築しながら、共通の機能を提供したい場合に適しています。
- コンポジションは、オブジェクト間の関係をより柔軟に制御したい場合に適しています。
java inheritance interface