Java的多态(Polymorphism)是面向对象编程中的一种特性,它允许不同的对象能够以统一的方式进行访问和操作。
它允许一个类的实例在运行时表现出多种形态。
Java多态的实现主要依赖于两个基本概念:继承和方法重写。
Java では、サブクラスは親クラスのメソッドを継承し、これらのメソッドをオーバーライドすることで独自の特定の動作を実装できます。オブジェクトを作成するとき、オブジェクトはそれ自体のクラス型、または任意の親クラスまたは実装されたインターフェイス型を指すことができます。これは Java ポリモーフィズムの核となる考え方です。つまり、オブジェクトは、使用されるコンテキストに応じて複数の外観と動作を持つことができます。
Java ポリモーフィズムの利点は、コードの再利用性と保守性が向上し、コードがより柔軟で拡張可能になることです。たとえば、メソッドは基本クラス (Object など) をパラメータとして受け入れ、実行時にサブクラスのインスタンスを渡すことができるため、メソッドを書き直すことなくさまざまなオブジェクト タイプを処理できます。
記事ディレクトリ
父类引用引用一个具有子类身份信息的对象时,就创建了一个多态关系的实现效果。
Java では、ポリモーフィズムは次の 2 つの方法で実装できます。
Parent obj = new Child();
obj.show(); // 调用子类的方法
表现出多态度的过程:多态性的优点在于它增加了代码的灵活性和可扩展性,使得代码更易于维护和重用。
-
コンパイル時ポリモーフィズム: 静的ポリモーフィズムとも呼ばれ、メソッドのオーバーロードによって実装されます。メソッドのオーバーロードとは、クラス内に同じ名前で異なるパラメーター リストを持つ複数のメソッドを定義することを指します。コンパイラーは、渡されたパラメーターのタイプに応じて呼び出すメソッドを選択します。
-
実行時ポリモーフィズム: 動的ポリモーフィズムとも呼ばれ、メソッドの書き換えによって実装されます。メソッドの書き換えとは、サブクラスが親クラス内の同じ名前とパラメーター リストを持つメソッドを再定義し、サブクラス オブジェクトが実行時に特定のオブジェクト タイプに従って対応するメソッドを呼び出すことを意味します。
簡単な説明:父类类型 类型名称 = 子类对象
実行時ポリモーフィズムの実装は、次の条件を満たす必要があります。
- 継承関係があります。子クラスは親クラスから継承します。
- メソッドのオーバーライドがあります。サブクラスは親クラスのメソッドをオーバーライドします。
- 親クラスの参照はサブクラスのオブジェクトを指します。親クラスの参照はサブクラスのオブジェクトを指し、実行時に実際のオブジェクトの型に応じて対応するメソッドが呼び出されます。
詳細:
-
親クラスと子クラスの間の関係の確立:
class Parent { void show() { System.out.println("Parent's show method"); } } class Child extends Parent { void show() { System.out.println("Child's show method"); } }
-
ポリモーフィズムを使用する場合:
Parent obj1 = new Parent(); Parent obj2 = new Child(); obj1.show(); // 调用父类的方法 obj2.show(); // 调用子类的方法
-
メソッドの書き換え (オーバーライド):
@Override void show() { System.out.println("Child's overridden show method"); }
-
親クラス参照はサブクラス メソッドを呼び出します。
Parent obj = new Child(); obj.show(); // 调用子类的方法
Java でポリモーフィズムを使用するのは、通常、オブジェクト指向プログラミングのコンテキストで、特に次の状況でです。
-
継承関係: 複数のクラスに継承関係がある場合、それらのオブジェクトはポリモーフィズムを通じて処理できるため、より柔軟なコード構造が実現され、コード ロジックが簡素化されます。
-
メソッド パラメーター: メソッドがさまざまなタイプのオブジェクトを受け取り、それらに対して同様の操作を実行する必要がある場合、ポリモーフィズムを使用するとコードがより一般的になり、拡張が容易になります。
-
メソッドの戻り値: メソッドの戻り値の型が基本クラスまたはインターフェイス型であるが、実際の戻り値が派生クラスのインスタンスである場合、ポリモーフィズムを使用して実現できます。
-
コレクションの使用: コレクション (List、Set、Map など) を使用すると、ポリモーフィズムを使用してさまざまなタイプのオブジェクトを格納でき、統一されたインターフェイスを通じて操作を実行できます。
一般に、ポリモーフィズムによりコードの柔軟性と保守性が向上し、コードの再利用性とスケーラビリティが向上します。
Java でポリモーフィズムを実現するには、次の点が必要です。
0.その他:
- アップキャスト: サブクラス オブジェクトを親クラス参照に割り当てます。これを行うとポリモーフィズムが有効になりますが、親クラスで定義されたメソッドのみを呼び出すことができます。
- ダウンキャスト: 親クラスの参照をサブクラス オブジェクトに変換します。変換にはキャストが必要です。ただし、ダウンキャストによって ClassCastException が発生する可能性があるため、キャストの前に型チェック (instanceof を使用) が必要であることに注意してください。
1. 継承関係(親クラスと子クラスの作成):
まず、いくつかの共通のプロパティとメソッドを含む親クラス (基本クラス) を設計する必要があります。次に、親クラスのプロパティとメソッドを継承し、独自の特定のプロパティとメソッドを追加できる 1 つ以上のサブクラス (派生クラス) を作成します。
class Shape {
void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Drawing a rectangle");
}
}
2. メソッドの書き換え (オーバーライド):
サブクラスでは、@Override
アノテーションを使用して親クラスのメソッドをオーバーライドできます。ポリモーフィズムで動作するために、オーバーライドされたメソッドが同じメソッド シグネチャ (名前、パラメーター リスト、戻り値の型) を持っていることを確認してください。
3. 親クラス参照とサブクラス オブジェクト:
親クラスの参照変数を使用してサブクラスのオブジェクトを参照することが、ポリモーフィズムの鍵となります。これにより、コーディング時の柔軟性を維持でき、実行時にどのクラス メソッドを呼び出すかを決定できます。
Shape myShape1 = new Circle();//`Shape` 是父类引用,而 `new Circle();` 是子类对象。
Shape myShape2 = new Rectangle();
myShape1.draw(); // 输出:Drawing a circle
myShape2.draw(); // 输出:Drawing a rectangle
ポリモーフィズムにより、サブクラス オブジェクトを親クラス参照に割り当てることができるため、親クラス参照を使用してサブクラス オブジェクトを参照できるようになります。これにより、コードの柔軟性と拡張性が容易になります。
Shape myShape1 = new Circle();
このコード行では、 Circle
class のオブジェクトを作成し、それをShape
type の参照変数に割り当てますmyShape1
。Circle
この割り当ては のサブクラスであるため、 Shape
正当です。これは、myShape1
の型は ですが Shape
、それが指すものは実際にはCircle
型のオブジェクトであることを意味します。
このようにして、共通の Shape 参照を使用して、さまざまな具体的なシェイプ オブジェクトを参照することができ、それによってポリモーフィズムと継承の概念を実現できます。この設計パターンにより、具体的なオブジェクトの特定の機能を保持しながら、コード内でより抽象的な方法でオブジェクトを操作できるようになります。
4. 実行時バインディング (実行時ポリモーフィズム):
メソッドが親クラス参照を通じて呼び出される場合、Java は実行時に呼び出す実際のメソッドを動的に決定します。これはランタイム バインディングと呼ばれます。これは、異なるコンテキストで同じメソッド呼び出しを使用できますが、実際には異なるサブクラスのメソッドを実行できることを意味します。
5. 抽象クラスまたはインターフェイスを使用します (オプション)。
メソッド シグネチャの共有セットを定義する場合は、抽象クラスまたはインターフェイスを使用します。抽象クラスは部分的な実装を提供できますが、インターフェイスはすべてのメソッドの実装を強制します。これにより、ポリモーフィズムがより柔軟になり、異なるクラス間でより多くの動作を共有できるようになります。
interface Sound {
void makeSound();
}
class Dog implements Sound {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat implements Sound {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
要約すると、ポリモーフィズムを実装するには、継承関係の作成、メソッドのオーバーライド、スーパークラス参照とサブクラス オブジェクトの使用、実行時バインディング、および場合によっては抽象クラスまたはインターフェイスが必要です。これらの概念により、ポリモーフィズムはオブジェクト指向プログラミングにおける強力な機能となり、コードの柔軟性と保守性が向上します。
多態性呼び出しメンバーの機能
変数呼び出し:コンパイル時に左側を見て、実行時に左側を見て
メソッド呼び出し:コンパイル時に左側を見て、実行時に右側を見て
-
変数がメンバーを呼び出す場合、アクセス可能なメンバーは変数の宣言された型 (左側) に従ってコンパイル時に決定され、変数の実際の型 (左側) は実行時に使用されて決定されます。実際に実行されたコード。
编译时看左边: 在编译阶段,编译器会检查父类中是否存在被调用的成员变量。 如果存在,编译通过;如果不存在,编译失败。 运行时也看左边: 在运行阶段,程序实际获取的是左边父类中的成员变量的值,而不考虑实际对象的类型。
-
メソッドを呼び出す際、コンパイル時に宣言された変数(左側)の型に応じて呼び出せるメソッドが決まりますが、実際に実行されるメソッドは実際のオブジェクトの型に応じて決まります(右側) 実行時。
编译时看左边: 在编译阶段,编译器会检查变量的声明类型(左边)来确定可调用的方法。 如果左边的类型没有声明被调用的方法,编译会报错,即使实际对象具有相应的方法。 运行时看右边: 在运行阶段,方法调用会根据实际对象的类型(右边)来决定实际执行的方法。 即使使用父类的引用,程序也会根据实际对象的类型来调用相应的方法,这被称为动态方法分派。
この動作により、実行時にオブジェクト インスタンスを置き換えることによってさまざまな動作を実装できます。これはポリモーフィズムの重要な概念です。
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal1 = new Dog(); // 编译看左边,运行也看左边
Animal myAnimal2 = new Cat(); // 编译看左边,运行也看左边
myAnimal1.makeSound(); // 编译看左边,运行看右边 ("Dog barks")
myAnimal2.makeSound(); // 编译看左边,运行看右边 ("Cat meows")
}
}
instanceofは一致判定成功後、指定されたクラスにオブジェクトをキャストします。
使用 instanceof 进行类型检查和强制类型转换时,应该确保类型转换是安全的,
即要确保对象的实际类型与你尝试转换的类型是相符的。
如果类型不匹配,会在运行时抛出 ClassCastException 异常。
方法 1
if (object instanceof MyClass) {
MyClass myObject = (MyClass) object;
// 此时可以使用 myObject 来访问 MyClass 特有的方法和属性
}
ここで、 object はチェックするオブジェクト、MyClass はチェックするクラス名です。object がクラス MyClass またはその派生クラスの 1 つのインスタンスである場合、条件は true となり、コード ブロックが実行されます。`
コード1: 特定のクラスかどうかの判定
class ParentClass {
// Contents of the parent class
}
class ChildClass1 extends ParentClass {
// Contents of the first child class
}
class ChildClass2 extends ParentClass {
// Contents of the second child class
}
public class Main {
public static void main(String[] args) {
ParentClass obj = new ParentClass(); // This can be an instance of any class
if (obj instanceof ChildClass1) {
System.out.println("obj is an instance of ChildClass1");
} else if (obj instanceof ChildClass2) {
System.out.println("obj is an instance of ChildClass2");
} else {
System.out.println("obj is not an instance of ChildClass1 or ChildClass2");
}
}
}
この例では、最初に ParentClass のインスタンスを作成し、次に、instanceof 演算子を使用して、それが ChildClass1 または ChildClass2 のインスタンスであるかどうかを確認します。そうでない場合は、対応するプロンプト情報を出力します。
コード2: マッチング後の強制転送
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("Dog barks");
}
public void fetch() {
System.out.println("Dog fetches the ball");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal;
myDog.fetch(); // 可以调用 Dog 特有的方法
}
myAnimal.makeSound(); // 输出:Dog barks
}
}
Fang 12: 条件判定における型チェックと型変換の同時実行
if (object instanceof MyClass myObject) {
// 此时可以使用 myObject 来访问 MyClass 特有的方法和属性
}
コード例:
class MyClass {
public void myMethod() {
System.out.println("MyClass method");
}
}
public class Main {
public static void main(String[] args) {
Object object = new MyClass();
if (object instanceof MyClass myObject) {
myObject.myMethod(); // 可以直接使用 myObject 调用 MyClass 的方法
}
}
}
オブジェクトの等価性チェック方法
ケース 1: サブクラスには独自の等価性の概念があり、getClass を使用して検出されます。
この場合、サブクラスは、スーパークラスとは異なる等価性の特定の定義を持つ可能性があります。getClass
したがって、オブジェクトのクラス型を検出し、それに応じて比較するメソッドを使用する必要があります。
class Shape {
// 父类 Shape
}
class Circle extends Shape {
private double radius; // 定义一个私有的double类型的成员变量radius,表示圆的半径
public Circle(double radius) {
this.radius = radius; // 构造函数,接受一个double类型的参数radius,并将其赋值给类的成员变量radius
}
@Override
public boolean equals(Object obj) {
// 重写equals方法
if (obj == this) {
return true; // 如果obj等于当前对象,返回true
}
if (obj == null || obj.getClass() != this.getClass()) {
return false; // 如果obj为null或者其类类型与当前对象的类类型不同,返回false
}
Circle other = (Circle) obj; // 将obj强制转换为Circle类型
return this.radius == other.radius; // 比较其radius成员变量与当前对象的radius成员变量是否相等,如果相等,则返回true,否则返回false
}
}
class Square extends Shape {
public double side; // 定义一个公有的double类型的成员变量side,表示正方形的边长
public Square(double side) {
this.side = side; // 构造函数,接受一个double类型的参数side,并将其赋值给类的成员变量side
}
@Override
public boolean equals(Object obj) {
// 重写equals方法
if (obj == this) {
return true; // 如果obj等于当前对象,返回true
}
if (obj == null || obj.getClass() != this.getClass()) {
return false; // 如果obj为null或者其类类型与当前对象的类类型不同,返回false
}
Square other = (Square) obj; // 将obj强制转换为Square类型
return this.side == other.side; // 比较其side成员变量与当前对象的side成员变量是否相等,如果相等,则返回true,否则返回false
}
}
public class Mains {
public static void main(String[] args) {
Circle circle1 = new Circle(5.0); // 创建一个Circle对象,circle1,半径为5.0
Circle circle2 = new Circle(5.0); // 创建一个Circle对象,circle2,半径为5.0
System.out.println(circle1.equals(circle2)); // 调用circle1的equals方法,将circle2作为参数传入,然后将返回的结果打印到控制台
}
}
分析する
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Circle other = (Circle) obj;
return this.radius == other.radius;
}
これはCircleクラスのequalsメソッドで、Object型のパラメータobjを受け入れます。このメソッドは、まず obj が現在のオブジェクトと等しいかどうかを確認し、等しい場合は true を返します。次に、obj が null かどうか、またはそのクラス タイプが現在のオブジェクトのクラス タイプと異なるかどうかを確認し、そうである場合は false を返します。最後に、obj を Circle 型にキャストし、その radius メンバー変数と現在のオブジェクトの radius メンバー変数を比較し、等しい場合は true を返し、等しくない場合は false を返します。
[ 0 ] この意味
この行ではif (obj == this)
、これは現在の Circle オブジェクトを表します。この判定ステートメントは、渡されたオブジェクト obj が現在のオブジェクトである場合 (つまり、obj と this が同じオブジェクトを参照している場合)、それらが等しいことを示す true が返されることを意味します。
この行ではreturn this.radius == other.radius;
、これは現在の Circle オブジェクトも表します。この return ステートメントは、現在の Circle オブジェクトの半径 (this.radius) が受信 Circle オブジェクト (other) の半径 (other.radius) と等しい場合に true を返し、2 つの Circle オブジェクトが等しいことを意味します。
[ 2 ](オブジェクトオブジェクト)
このequalsメソッドの定義では、Object objがメソッドのパラメータです。equals メソッドを呼び出してパラメーターを渡すと、パラメーターの値が obj に割り当てられます。
たとえば、circle1.equals(circle2) を呼び出すと、circle2 の参照が obj に割り当てられます。次に、equals メソッド内で、obj を使用して、渡されたオブジェクトにアクセスできます。
要約:
ケース 1 では、比較されるオブジェクトが同じクラスのインスタンスであることを確認するために、getClass() メソッドを使用します。これは、Java では、equals() メソッドのパラメータが Object 型であり、任意のオブジェクトをパラメータとして受け入れることができるためです。したがって、equals() メソッドで型チェックを実行して、渡されたオブジェクトが現在のオブジェクトと同じクラスのインスタンスであることを確認する必要があります。
ケース 1 では、渡されたオブジェクトが現在のオブジェクトと同じオブジェクトであるかどうかを最初に確認し、同じオブジェクトである場合は true を返します。オブジェクトは常にそれ自体と等しいため、これは最適化のステップです。
次に、getClass() メソッドを使用して、渡されたオブジェクトのクラス型が現在のオブジェクトのクラス型と同じかどうかを確認します。そうでない場合は、渡されたオブジェクトが現在のオブジェクトのサブクラスまたはスーパークラスではないことを意味するため、これらは等しくあり得ず、 false が返されます。
ケース 1 では、比較対象のオブジェクトが同じクラスのインスタンスであることが保証されるため、getClass() メソッドによる型チェックが一般的です。これにより、比較中の型の不一致エラーが回避されます。
型チェックに getClass() メソッドを使用する場合、渡されるオブジェクトは null ではないと想定されることに注意してください。したがって、型チェックを行う前に、渡されたオブジェクトが null かどうかをチェックし、null であれば false を返します。
要約すると、ケース 1 では、比較されるオブジェクトが同じクラスのインスタンスであることを確認するために、型チェックに getClass() メソッドを使用する必要があります。これにより、型の不一致エラーが回避され、equals() メソッドの正確さが保証されます。
ケース 2: 等価性の概念はスーパークラスによって決定され、instanceof は以下を検出するために使用されます。
この場合、スーパークラスがオブジェクトの同等性の定義を決定し、異なるサブクラスのオブジェクトも同等であると見なすことができます。instanceof
キーワードを使用して、オブジェクトが特定のクラスのインスタンスであるかどうかを検出できます。
class Animal {
// 父类 Animal
protected String species;
public Animal(String species) {
this.species = species;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !(obj instanceof Animal)) {
return false;
}
Animal other = (Animal) obj;
return this.species.equals(other.species);
}
}
class Dog extends Animal {
public Dog() {
super("Dog");
}
}
class Cat extends Animal {
public Cat() {
super("Cat");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
System.out.println(dog.equals(cat)); // false,因为它们的种类不同
}
}
これらの例は、オブジェクトのタイプと等価性の定義に応じて、さまざまな状況でオブジェクトの等価性比較を実装する方法を示しています。
false理由:
Dog と Cat は Animal のサブクラスであるため、親クラス Animal の equals() メソッドを継承します。この場合、それらの種のプロパティが等しくないため、dog.equals(cat) は false を返します。
したがって、ケース2では、サブクラス間でequals()メソッドの比較を行い、親クラスから継承した属性を比較することでオブジェクトが等しいかどうかを判断します。
等しい
Animal クラスでは、オブジェクトが等しいかどうかを比較するために、equals() メソッドがオーバーライドされます。equals() メソッドでは、最初に受信オブジェクトが現在のオブジェクトと同じオブジェクトかどうかを確認し、同じオブジェクトである場合は true を返します。次に、instanceof を使用して、渡されたオブジェクトが Animal クラスまたはそのサブクラスのインスタンスであるかどうかを確認します。そうでない場合、または渡されたオブジェクトが null の場合は、false を返します。最後に、受信オブジェクトを Animal タイプにキャストし、それらの種プロパティを比較して等しいかどうかを確認します。
Java.util.Array: 長さ、位置要素のフェーズ --> true
equals()
メソッドのプロトタイプは次のとおりです。
public static boolean equals(type[] a, type[] b)
プリミティブ データ型の配列 (int[]、double[]、char[] など)。
参照データ型配列 (String[]、Object[]、カスタム クラス配列など)。
java.lang.Object
クラスのメソッドのプロトタイプはequals
次のとおりです。
public boolean equals(Object obj)
equals
メソッドは、2 つのオブジェクトが等しいかどうかを比較するために使用されます。デフォルトでは、クラス内のオブジェクトの参照アドレスを使用してequals
メソッドが比較されます。つまり、2 つのオブジェクトが同じメモリ アドレスを参照しているかどうかが判断されます。ただし、多くの場合、オブジェクトの内容に基づいてそれらが等しいかどうかを判断する必要があるため、多くのカスタム クラスでは、メソッドをオーバーライドしてデフォルトの比較動作を変更するObject
必要があります。equals
equals
メソッドをオーバーライドするには、通常、次の条件を満たす必要があります。
- 反射性: null 以外の参照値の場合
x
、x.equals(x)
それが返される必要がありますtrue
。 - 対称性: null 以外の参照値
x
およびについては、 が返される場合y
に限り、が返される必要があります。y.equals(x)
true
x.equals(y)
true
- 推移性: null 以外の参照値および の場合
x
、がを返し、さらに を返す場合、 を返す必要があります。y
z
x.equals(y)
true
y.equals(z)
true
x.equals(z)
true
- 一貫性: null 以外の参照値
x
およびについて、y
2 つのオブジェクトのプロパティが変更されていない場合、複数の呼び出しはx.equals(y)
常に同じ結果を返す必要があります。 - null 以外の参照値の場合は
x
、x.equals(null)
を返す必要がありますfalse
。
実際の使用では、特にカスタムクラスでは、equals
参照アドレスの比較だけでなく、オブジェクトのプロパティに応じてメソッドを実装する必要があります。これにより、定義した等価条件に従って 2 つのオブジェクトが等しいと判断されます。
状況に適応する:
特定の状況に応じてどのケースを使用するかを決定し、equals() メソッドの正確さと期待される動作を保証する必要があります。
ケース 1: サブクラスには独自の等価性の概念があり、getClass() を使用して検出されます。
选择使用哪种情况取决于你的需求和设计。
如果子类具有自己的相等性概念,并且需要比较特定的属性来确定对象是否相等,那么情况1是更合适的选择。
- オブジェクトが等しいかどうかを判断するために特定のプロパティまたは状態を比較する必要があるカスタムの等価概念を持つサブクラスに役立ちます。
- サブクラスは、親クラスの equals() メソッドをオーバーライドし、getClass() メソッドを使用して、渡されたオブジェクトが同じクラスのインスタンスであるかどうかを確認します。
- オブジェクトは、特定のプロパティを比較することによって等しいかどうかテストされます。
ケース 2: 等価性の概念はスーパークラスによって決定され、instanceof は以下を検出するために使用されます。
如果超类已经定义了对象的相等性概念,并且子类继承了这个概念,那么情况2是更合适的选择。
- オブジェクトの平等の概念を定義するスーパークラスに適用され、サブクラスはこの平等の概念を継承します。
- スーパークラスは、equals() メソッドを定義し、instanceof を使用して、渡されたオブジェクトがスーパークラスまたはそのサブクラスのインスタンスであるかどうかを確認します。
- オブジェクトは、スーパークラスによって定義されたプロパティを比較することによって等しいかどうかテストされます。