目次
抽象クラス
クラスを定義する場合、クラスの動作特性を記述するためにいくつかのメンバー メソッドを定義する必要があることがよくありますが、これらのメソッドの実装を決定できない場合があります。たとえば、先ほどAnimalクラスを定義する際に、動物の鳴き声を表すためにshout()メソッドを使用しましたが、動物が異なれば鳴き声も異なるため、動物の鳴き声をshout()メソッドで正確に記述することはできません。
上で説明した状況に対して、Java はこの要求を満たす抽象メソッドを提供します。抽象メソッドは、abstract キーワードで装飾されたメンバー メソッドであり、抽象メソッドの定義時にメソッド本体を実装する必要はありません。
抽象メソッドの定義形式は次のとおりです。
修饰符 abstract void 方法名(参数列表);
抽象メソッドは、動作仕様の定義に使用される抽象クラスまたはインターフェイスに含めることができ、特定の実装はサブクラスによって完成されます。抽象メソッドにはメソッド本体はなく、セミコロン (;) で終了するメソッド宣言のみがあります。
知らせ
抽象メソッドを使用する場合は、次の点に留意する必要があります。
- 抽象メソッドは抽象クラスまたは抽象インターフェイスにのみ存在でき、通常のクラスでは定義できません。
- 抽象クラスはインスタンス化できません (初心者が犯しやすい間違いです)。インスタンス化すると、エラーが報告され、コンパイルは失敗します。抽象クラスの非抽象サブクラスのみがオブジェクトを作成できます。
- 抽象クラスには必ずしも抽象メソッドが含まれる必要はありませんが、抽象メソッドを持つクラスは抽象クラスである必要があります。
- 抽象クラスの抽象メソッドは単なる宣言であり、メソッド本体は含まれません。つまり、メソッドの特定の実装、つまりメソッドの特定の機能は提供されません。
- abstract キーワードで修飾された抽象メソッドは、private、static、final、native で修飾することはできません。抽象メソッドはサブクラスによって実装される必要があり、宣言に使用される場合、サブクラスはメソッドを実装できないためです。
- サブクラス内の抽象メソッドをオーバーライドする場合は、 @Override アノテーションを使用してメソッドのオーバーライドを識別する必要があります。
- サブクラスがすべての抽象メソッドを完全に実装していない場合でも、サブクラスを抽象クラスとして宣言する必要があります。
以下は、抽象メソッドの使用を示す簡単な例です。
// 定义一个抽象类 Shape
abstract class Shape {
// 定义一个抽象方法 calculateArea
public abstract double calculateArea();
}
// Circle 类继承自 Shape 抽象类
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
// 实现抽象方法 calculateArea,计算圆的面积
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// Rectangle 类继承自 Shape 抽象类
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
// 实现抽象方法 calculateArea,计算矩形的面积
@Override
public double calculateArea() {
return width * height;
}
}
public class myclass {
public static void main(String[] args) {
// 创建一个 Circle 对象,并计算其面积
Circle circle = new Circle(5.0);
double circleArea = circle.calculateArea();
System.out.println("圆的面积:" + circleArea);
// 创建一个 Rectangle 对象,并计算其面积
Rectangle rectangle = new Rectangle(4.0, 6.0);
double rectangleArea = rectangle.calculateArea();
System.out.println("矩形的面积:" + rectangleArea);
}
}
この例では、抽象クラス Shape が定義されており、これには抽象メソッド CalculateArea が含まれています。抽象クラスはインスタンス化できないため、抽象メソッドを実装するには具体的なサブクラスを作成する必要があります。CircleクラスとRectangleクラスはそれぞれ抽象クラスShapeを継承し、円と四角形の面積を計算するcalculateAreaメソッドを書き換えます。
main 関数では、Circle オブジェクトと Rectangle オブジェクトを作成し、それらの CalculateArea メソッドを呼び出して面積を計算し、結果を出力します。
操作結果:
圆的面积:78.53981633974483
矩形的面积:24.0
インターフェース
抽象クラスのすべてのメソッドが抽象である場合、クラスはインターフェイスを定義できます。インターフェイスは Java の最も重要な概念の 1 つであり、インターフェイスはグローバル定数とパブリック抽象メソッドで構成される特別なクラスであり、通常のメソッドを含めることはできません。
- JDK8 より前は、インターフェイスはグローバル定数と抽象メソッドで構成されており、インターフェイス内の抽象メソッドはメソッド本体を持つことができませんでした。
- JDK 8 はインターフェイスを再定義します。抽象メソッドに加えて、インターフェイスにはデフォルト メソッドと静的メソッド (クラス メソッドとも呼ばれます) も含めることができます。デフォルト メソッドはデフォルトで変更され、静的メソッドは static によって変更されます。両方のメソッドで許可されます。メソッド本体があります。詳細については、 「Java 8 のデフォルト メソッド」を参照してください。
- JDK 9 以降では、一部の再利用コードがメソッドを公開しないように、メソッドをprivateとして定義することが許可されています。詳細については、 「Java 9 プライベート インターフェイス メソッド」を参照してください。
インターフェースの宣言
インターフェイスは、interface キーワードを使用して宣言され、構文は次のとおりです。
import java.lang.*;
//引入包
public interface 接口名
{
//任何类型 final, static 字段
//抽象方法
}
インターフェイスには次のプロパティがあります。
- インターフェイスは暗黙的に抽象です。インターフェイスを宣言すると、デフォルトで抽象とみなされ、キーワード abstract で修飾する必要はありません。
- インターフェイス内のメソッドは暗黙的に抽象です。インターフェイス内で定義されたメソッドもデフォルトで抽象とみなされ、abstract キーワードで修飾する必要はありません。インターフェイス内のメソッドにはメソッド シグネチャのみがあり、メソッド本体はありません。特定の実装はインターフェイスを実装するクラスによって提供されます。
- インターフェイス内のメソッドはパブリックです。インターフェイス内で定義されたメソッドは、デフォルトでパブリック (パブリック) アクセス権を持ち、インターフェイスを実装するクラスによってパブリックにアクセスして呼び出すことができます。
例
interface Animal {
public void eat();
public void travel();
}
インターフェースの使用
抽象クラスと同様に、インターフェイスの使用はサブクラスを通じて行う必要があり、サブクラスはimplements キーワードを通じてインターフェイスを実装し、サブクラスはインターフェイス内のすべての抽象メソッドを実装する必要があります。クラスは同時に複数のインターフェイスを実装でき、複数のインターフェイスは英語のカンマ (,) で区切る必要があることに注意してください。
インターフェイスの実装クラスを定義します。構文形式は次のとおりです。
修饰符 class 类名 implements 接口1,接口2,...{
...
}
例
// 定义一个接口 Animal
interface Animal {
// 定义抽象方法
void eat();
void sleep();
}
// 实现 Animal 接口的 Cat 类
class Cat implements Animal {
// 实现接口中的抽象方法
@Override
public void eat() {
System.out.println("猫在吃东西");
}
@Override
public void sleep() {
System.out.println("猫在睡觉");
}
}
// 实现 Animal 接口的 Dog 类
class Dog implements Animal {
// 实现接口中的抽象方法
@Override
public void eat() {
System.out.println("狗在吃东西");
}
@Override
public void sleep() {
System.out.println("狗在睡觉");
}
}
public class Main {
public static void main(String[] args) {
Animal cat = new Cat();
cat.eat();
cat.sleep();
Animal dog = new Dog();
dog.eat();
dog.sleep();
}
}
上記の例をコンパイルして実行した結果は次のようになります。
猫在吃东西
猫在睡觉
狗在吃东西
狗在睡觉
インターフェイスで宣言されたメソッドをオーバーライドする場合は、次の規則に従う必要があります。
- クラスがインターフェイス メソッドを実装する場合、インターフェイス メソッドよりも幅広い例外の種類 (必須例外) をスローすることはできず、インターフェイスまたはインターフェイスを継承する抽象クラスで例外をスローすることを宣言することしかできません。
- クラスは、インターフェイス メソッドをオーバーライドするときに、メソッド名、パラメーター リスト、戻り値の型を含む同じメソッド シグネチャを維持する必要があります。
- クラスが抽象クラスで、インターフェイスのいくつかのメソッドをすでに実装している場合は、インターフェイスのメソッドを再度実装する必要はありません。
インターフェイスを実装するときは、次のような注意すべきルールもあります。
- クラスは、カンマで区切って複数のインターフェイスを同時に実装できます。
- クラスは 1 つのクラスからのみ継承できますが (単一継承)、複数のインターフェイスを実装できます。
- インターフェイス間に継承関係が存在することもあり、キーワード extends を使用して、インターフェイスは別のインターフェイスから継承できます。このようにして、サブインターフェイスは親インターフェイスのメソッド宣言を継承します。
インターフェースの継承
インターフェイスは別のインターフェイスを継承できます。これは、クラス間の継承方法と同様です。インターフェイスの継承にはextends キーワードを使用し、サブインターフェイスは親インターフェイスのメソッドを継承します。
public interface 子接口名 extends 父接口名 {
// 子接口的其他方法和常量声明
}
インターフェイスの継承により、サブインターフェイスは親インターフェイスのすべてのメソッド宣言を持つことができ、サブインターフェイス内で独自のメソッドと定数を定義できます。インターフェイスはカンマで区切って複数のインターフェイスを継承できることに注意してください。例えば:
public interface 子接口名 extends 父接口1, 父接口2, ... {
// 子接口的其他方法和常量声明
}
このようにして、サブインターフェイスは複数の親インターフェイスのメソッド宣言を同時に継承できます。インターフェイスの継承は、コード構造の整理と拡張に役立ち、コードの再利用性と保守性を向上させることができます。
以下に、インターフェースの継承を示す例を示します。
//定义父接口A
interface InterfaceA {
void methodA(); // 父接口A的方法声明
}
//定义子接口B,继承自父接口A
interface InterfaceB extends InterfaceA {
void methodB(); // 子接口B的方法声明
}
//实现子接口B的类
class MyClass implements InterfaceB {
@Override
public void methodA() {
System.out.println("实现了父接口A的方法");
}
@Override
public void methodB() {
System.out.println("实现了子接口B的方法");
}
}
//测试代码
public class My {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.methodA(); // 调用父接口A的方法 ,输出结果:实现了父接口A的方法
obj.methodB(); // 调用子接口B的方法 ,输出结果:实现了子接口B的方法
}
}
開発中にサブクラスがインターフェイスを実装し、抽象クラスを継承する必要がある場合、サブクラスは次の形式で定義できます。
修饰符 class 子类名 extends 父类名 implements 接口1, 接口2, ... {
// 类的成员和方法定义
}
上記の形式では、サブクラスは extends キーワードを使用して指定された親クラスを継承し、implements キーワードを使用して指定されたインターフェイスを実装します。抽象クラス(親クラス)を継承し、複数のインターフェースを同時に実装することが可能です。
注: Java では単一継承のみがサポートされています。つまり、クラスは 1 つの親クラスからのみ直接継承できます。ただし、複数のインターフェースを実装することで多重継承の効果が得られ、クラスが複数のインターフェースの機能を持つことができます。
以下は、サブクラスが抽象クラスを継承し、2 つのインターフェイスを実装する場合のサンプル コードです。
abstract class AbstractClass {
abstract void methodA();
}
interface InterfaceA {
void methodB();
}
interface InterfaceB {
void methodC();
}
class Subclass extends AbstractClass implements InterfaceA, InterfaceB {
@Override
void methodA() {
System.out.println("实现了抽象类中的方法");
}
@Override
public void methodB() {
System.out.println("实现了接口A中的方法");
}
@Override
public void methodC() {
System.out.println("实现了接口B中的方法");
}
}
public class Main {
public static void main(String[] args) {
Subclass obj = new Subclass();
obj.methodA(); // 调用抽象类中的方法 ,输出结果:实现了抽象类中的方法
obj.methodB(); // 调用接口A中的方法 ,输出结果:实现了接口A中的方法
obj.methodC(); // 调用接口B中的方法 ,输出结果:实现了接口B中的方法
}
}
マーカーインターフェイス
最も一般的な継承インターフェイスは、メソッドを含まないインターフェイスです。
マーカー インターフェイスはメソッドやプロパティを持たないインターフェイスであり、クラスを特定の型に属するか特定の特権を持つものとしてマークするためにのみ使用されます。マーカー インターフェイスは、クラスをインターフェイス型に変換することで、クラスにデータ型指定を追加します。
マーカー インターフェイスの役割は、次の 2 つの側面に要約できます。
- パブリック親インターフェイスを確立する: マーカー インターフェイスは、他のインターフェイスによって継承されるパブリック親インターフェイスとして使用できます。たとえば、Java API のインターフェイスは、
java.util.EventListener
他の多くのインターフェイスが間接的に継承するマーカー インターフェイスです。マーカーインターフェイスを継承することで、一連のインターフェイスの統一的な制約と仕様を実現できます。 - クラスにデータ型を追加する: マーカー インターフェイスを実装することで、クラスはプログラム内での判断や処理のために特定の種類の ID を取得できます。ポリモーフィズムにより、マーカー インターフェイスを実装するクラスがインターフェイス型に変換され、特定の種類の操作を実行したり、特定の権限を割り当てたりできるようになります。これにより、クラスの分類、編成、処理が容易になります。
一般的な例:
- java.io.Serializable: このインターフェイスを実装していないクラスは、オブジェクトをシリアル化および逆シリアル化できません。さまざまな Java コンパイラー実装間で一貫性を確保するには、シリアライザー クラスで明示的な SerialVersionUID 値を宣言する必要があります。
import java.io.Serializable; public class MyClass implements Serializable { // 类的实现... }
- java.lang.Cloneable: Object.clone() メソッドがこのクラスのインスタンスをフィールドごとに合法的にコピーできることを示します。このインターフェイスを実装するクラスは、パブリック メソッド (保護されている) で Object.clone() をオーバーライドする必要があります。Cloneable インターフェイスを実装していないインスタンスで Object の clone() メソッドが呼び出された場合、CloneNotSupportedException がスローされます。
public class MyClass implements Cloneable { @Override public Object clone() throws CloneNotSupportedException { // 对象的复制逻辑... } }
- java.util.RandomAccess: 高速 (通常は一定時間) のランダム アクセスをサポートしていることを識別するために使用されます。このインターフェイスを実装すると、一般的なアルゴリズムの動作が変更され、ランダム アクセス リストまたはシーケンシャル アクセス リストに適用された場合のパフォーマンスが向上します。
import java.util.ArrayList; import java.util.List; import java.util.RandomAccess; public class MyClass { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); if (list instanceof RandomAccess) { // 列表支持快速随机访问的处理逻辑... } } }
- java.rmi.Remote: リモート インターフェイスは、非ローカル仮想マシンからメソッドを呼び出すことができるインターフェイスを識別するために使用されます。すべてのリモート オブジェクトはこのインターフェイスを直接的または間接的に実装する必要があり、java.rmi.Remote を拡張するインターフェイスで指定されたメソッドのみがリモートで使用可能になります。
import java.rmi.Remote; import java.rmi.RemoteException; public interface MyRemoteInterface extends Remote { void performRemoteAction() throws RemoteException; }
これらの識別インターフェイスは主に、コード内の型クエリと処理、および特定のランタイム環境またはコンテナでの識別と処理に使用されます。
マーカー インターフェイスはメソッドを定義していませんが、インターフェイスを実装することにより、クラスが特定の特性またはプロパティを持っていることを示すことに注意してください。マーカー インターフェイスの一般的なアプリケーション シナリオには、イベント監視、シリアル化識別、権限識別などが含まれます。
つまり、マーカー インターフェイスは軽量の動作マーキング メカニズムであり、クラスを特定のマークでマークするインターフェイスを実装するだけで、プログラム内で分類および処理できます。
抽象クラスと抽象インターフェイスを使用する場合
-
抽象クラスを使用します。
- 抽象クラスは、共通のプロパティとメソッドを持つ関連クラスのグループがあり、これらのメソッドの一部にデフォルト実装を持たせたい場合に使用されます。抽象クラスでは具体的に実装されたメソッドと抽象メソッドを定義でき、サブクラスは抽象クラスを継承した後に具体的に実装されたメソッドを直接利用したり、必要に応じて抽象メソッドを書き換えたりすることができます。
- 抽象クラスは、基本機能が比較的安定している状況に適していますが、基本機能が頻繁に変更される場合は、抽象クラスを実装するすべてのサブクラスを変更する必要があります。
-
インターフェースを使用します。
- インターフェースは、多重継承を実装する場合、または複数のクラス間の統一仕様に準拠する必要がある場合に使用できます。インターフェイスは実装する必要がある一連のメソッドを定義し、クラスはインターフェイスを実装することで仕様に準拠するという目的を達成できます。
- インターフェイスは、異なるクラスが動作を共有する必要があるが継承関係がない場合に適しており、クラスは複数のインターフェイスを実装して多重継承の効果を実現できます。
- 基本関数を頻繁に変更する必要がある場合は、インターフェイスを実装するクラスのみを変更する必要があり、他のクラスを変更する必要がないため、インターフェイスを使用するとより柔軟になります。
要約すると、抽象クラスまたはインターフェイスの選択は、特定の状況に応じて判断する必要があります。デフォルトの実装があり、基本関数が比較的安定しており、多重継承が必要ない場合は、抽象クラスを選択できます。多重継承が必要な場合、複数のクラス間で統一仕様に従う必要がある場合、または基本機能を頻繁に変更する必要がある場合は、インターフェイスを選択する方が適切です。