Javaでのオーバーライドとオーバーライドの違いについて混乱しています。誰がこれらの違いについて詳細を提供できますか? Javaチュートリアル を読みましたが、サンプルコードはまだ混乱しています。
より明確にするために、オーバーライドをよく理解しています。私の問題は、1つがインスタンスレベルにあり、もう1つがクラスレベルにあるという事実を除いて、非表示に違いはないことです。
Javaチュートリアルコード:
public class Animal {
public static void testClassMethod() {
System.out.println("Class" + " method in Animal.");
}
public void testInstanceMethod() {
System.out.println("Instance " + " method in Animal.");
}
}
次に、サブクラスcatがあります。
public class Cat extends Animal {
public static void testClassMethod() {
System.out.println("The class method" + " in Cat.");
}
public void testInstanceMethod() {
System.out.println("The instance method" + " in Cat.");
}
public static void main(String[] args) {
Cat myCat = new Cat();
Animal myAnimal = myCat;
Animal.testClassMethod();
myAnimal.testInstanceMethod();
}
}
それから彼らは言う:
このプログラムからの出力は次のとおりです。
Animalのクラスメソッド。
Catのインスタンスメソッド。
私にとって、Animalクラスから直接testClassMethod()クラスメソッドを呼び出して、Animalクラスのメソッドを実行するという事実は明らかであり、特別なことは何もありません。次に、myCatへの参照からtestInstanceMethod()を呼び出します。そのため、実行されるメソッドがCatのインスタンス内のメソッドであることも明らかです。
私が見るものから、呼び出しの隠蔽はオーバーライドのように動作するので、なぜその区別をするのですか?上記のクラスを使用してこのコードを実行した場合:
Cat.testClassMethod();
取得します:Catのクラスメソッドしかし、CatからtestClassMethod()を削除すると、取得します:Animalのクラスメソッド
これは、親と同じ署名を使用して、サブクラスで静的メソッドを記述すると、ほとんどオーバーライドされることを示しています。
うまくいけば、自分が混乱している場所を明確にして、誰かが光を当てることができるようになります。事前に感謝します!
オーバーライドは基本的に遅延バインディングをサポートします。したがって、どのメソッドが呼び出されるかは実行時に決定されます。これは非静的メソッド用です。非表示は、他のすべてのメンバー(静的メソッド、インスタンスメンバー、静的メンバー)用です。それは早期バインディングに基づいています。より明確に、呼び出されるか使用されるメソッドまたはメンバーはコンパイル時に決定されます。
あなたの例では、最初の呼び出しAnimal.testClassMethod()
はstatic
メソッドの呼び出しです。したがって、どのメソッドが呼び出されるかは確実です。
2番目の呼び出しmyAnimal.testInstanceMethod()
では、非静的メソッドを呼び出します。これは、ランタイムポリモーフィズムと呼ばれるものです。どのメソッドを呼び出すかは、実行時まで決定されません。
詳細については、 this を参照してください。
静的メソッドは非表示になり、非静的メソッドはオーバーライドされます。呼び出しが「something()」と「this.something()」のどちらでもない場合、違いは顕著です。
私は本当に言葉にそれを置くように思えないので、ここに例があります:
public class Animal {
public static void something() {
System.out.println("animal.something");
}
public void eat() {
System.out.println("animal.eat");
}
public Animal() {
// This will always call Animal.something(), since it can't be overriden, because it is static.
something();
// This will call the eat() defined in overriding classes.
eat();
}
}
public class Dog extends Animal {
public static void something() {
// This method merely hides Animal.something(), making it uncallable, but does not override it, or alter calls to it in any way.
System.out.println("dog.something");
}
public void eat() {
// This method overrides eat(), and will affect calls to eat()
System.out.println("dog.eat");
}
public Dog() {
super();
}
public static void main(String[] args) {
new Dog();
}
}
出力:
animal.something
dog.eat
これは、オーバーライドと非表示の違いです。
私があなたの質問を適切に理解している場合、答えは「あなたはすでにオーバーライドしています」です。
「サブクラスで、親と同じ名前の静的メソッドを記述すると、ほとんどオーバーライドされることを示しています。」
スーパークラスのメソッドとまったく同じ名前のメソッドをサブクラスに記述すると、スーパークラスのメソッドがオーバーライドされます。メソッドをオーバーライドするために@Overrideアノテーションは必要ありません。ただし、コードをより読みやすくし、メソッドを実際にオーバーライドしていることをコンパイラに確認させます(たとえば、サブクラスメソッドのスペルを間違えませんでした)。
オーバーライドはインスタンスメソッドでのみ発生します。参照変数の型がAnimalで、オブジェクトがCatの場合、インスタンスメソッドがCatから呼び出されます(これはオーバーライドされます)。同じacatオブジェクトに対して、Animalのクラスメソッドが使用されます。
public static void main(String[] args) {
Animal acat = new Cat();
acat.testInstanceMethod();
acat.testClassMethod();
}
出力は次のとおりです。
The instance method in Cat.
Class method in Animal.
public class First {
public void Overriding(int i) { // will be overrided in class Second }
public static void Hiding(int i) { // will be hidden in class Second
// because it's static }
}
public class Second extends First {
public void Overriding(int i) { // overrided here }
public static void Hiding(int i) { // hidden
// because it's static }
}
記憶するためのルールは単純です:クラスを拡張するメソッドは、staticをvoidに変更できず、voidをstaticに変更できません。コンパイルエラーの原因になります。
しかしvoid Nameがvoid Nameに変更された場合、それはオーバーライドされます。
静的名が静的名に変更された場合、非表示になります。 (コンパイラがスーパークラスのオブジェクトでstaticメソッドを検出すると、サブクラスのメソッドをチェックしません。)
このコードスニペットでは、「静的」ではなく「プライベート」アクセス修飾子を使用して、メソッドの非表示とオーバーライドの違いを示しています。
class Animal {
// Use 'static' or 'private' access modifiers to see how method hiding work.
private void testInstancePrivateMethod(String source) {
System.out.println("\tAnimal: instance Private method calling from "+source);
}
public void testInstanceMethodUsingPrivateMethodInside() {
System.out.println("\tAnimal: instance Public method with using of Private method.");
testInstancePrivateMethod( Animal.class.getSimpleName() );
}
// Use default, 'protected' or 'public' access modifiers to see how method overriding work.
protected void testInstanceProtectedMethod(String source) {
System.out.println("\tAnimal: instance Protected method calling from "+source);
}
public void testInstanceMethodUsingProtectedMethodInside() {
System.out.println("\tAnimal: instance Public method with using of Protected method.");
testInstanceProtectedMethod( Animal.class.getSimpleName() );
}
}
public class Cat extends Animal {
private void testInstancePrivateMethod(String source) {
System.out.println("Cat: instance Private method calling from " + source );
}
public void testInstanceMethodUsingPrivateMethodInside() {
System.out.println("Cat: instance Public method with using of Private method.");
testInstancePrivateMethod( Cat.class.getSimpleName());
System.out.println("Cat: and calling parent after:");
super.testInstanceMethodUsingPrivateMethodInside();
}
protected void testInstanceProtectedMethod(String source) {
System.out.println("Cat: instance Protected method calling from "+ source );
}
public void testInstanceMethodUsingProtectedMethodInside() {
System.out.println("Cat: instance Public method with using of Protected method.");
testInstanceProtectedMethod(Cat.class.getSimpleName());
System.out.println("Cat: and calling parent after:");
super.testInstanceMethodUsingProtectedMethodInside();
}
public static void main(String[] args) {
Cat myCat = new Cat();
System.out.println("----- Method hiding -------");
myCat.testInstanceMethodUsingPrivateMethodInside();
System.out.println("\n----- Method overriding -------");
myCat.testInstanceMethodUsingProtectedMethodInside();
}
}
出力:
----- Method hiding -------
Cat: instance Public method with using of Private method.
Cat: instance Private method calling from Cat
Cat: and calling parent after:
Animal: instance Public method with using of Private method.
Animal: instance Private method calling from Animal
----- Method overriding -------
Cat: instance Public method with using of Protected method.
Cat: instance Protected method calling from Cat
Cat: and calling parent after:
Animal: instance Public method with using of Protected method.
Cat: instance Protected method calling from Animal
実行時に、オーバーライドされたメソッドの子バージョンは、メソッド呼び出しが親または子クラスメソッドで定義されているかどうかに関係なく、インスタンスに対して常に実行されます。この方法では、構文ParentClassName.method()を使用して、親メソッドへの明示的な呼び出しが参照されない限り、親メソッドは使用されません。または、メソッドの呼び出しが親クラスで定義されている場合、実行時に隠しメソッドの親バージョンが常に実行されます。
静的メソッドの非表示はJavaでどのように起こっていますか? CatクラスはAnimalクラスを拡張しています。したがって、Catクラスには両方の静的メソッドがあります(つまり、子クラスの静的メソッドと親クラスの静的メソッドを意味します)しかし、どのようにJVMが親静的メソッドを非表示にしますか?ヒープとスタックをどのように扱っていますか?
メソッドオーバーライドでは、メソッドの解決はランタイムオブジェクトに基づいてJVMによって行われます。一方、メソッドの非表示では、メソッドの解決は参照に基づいてコンパイラーによって行われます。したがって、
コードが次のように記述されている場合、
public static void main(String[] args) {
Animal myCat = new Cat();
myCat.testClassMethod();
}
出力は次のようになります。
Animalのクラスメソッド。
最近のJava研究に基づいて
OCP Java 7 book、page 70-71からの例:
public class Point {
private int xPos, yPos;
public Point(int x, int y) {
xPos = x;
yPos = y;
}
public boolean equals(Point other){
.... sexy code here ......
}
public static void main(String []args) {
Point p1 = new Point(10, 20);
Point p2 = new Point(50, 100);
Point p3 = new Point(10, 20);
System.out.println("p1 equals p2 is " + p1.equals(p2));
System.out.println("p1 equals p3 is " + p1.equals(p3));
//point's class equals method get invoked
}
}
しかし、次のメインを書くと:
public static void main(String []args) {
Object p1 = new Point(10, 20);
Object p2 = new Point(50, 100);
Object p3 = new Point(10, 20);
System.out.println("p1 equals p2 is " + p1.equals(p2));
System.out.println("p1 equals p3 is " + p1.equals(p3));
//Object's class equals method get invoked
}
2番目のメインでは、Objectクラスを静的型として使用しているため、Pointオブジェクトでequalメソッドを呼び出すと、Pointクラスがパラメーターとして到着するのを待っていますが、Objectは到着します。したがって、Objectクラスは、equals(Object o)があるため、実行されるメソッドに等しくなります。この場合、Pointのクラスはオーバーライドしませんが、Objectクラスのequalsメソッドを非表示にしますと等しくなります。
リンクされたJavaチュートリアルページは、オーバーライドと非表示の概念を説明しています
スーパークラスのインスタンスメソッドと同じシグネチャ(名前、パラメータの数と型)、および戻り値の型を持つサブクラスのインスタンスメソッドは、スーパークラスのメソッドをオーバーライドします。
サブクラスがスーパークラスの静的メソッドと同じシグネチャを持つ静的メソッドを定義する場合、サブクラスのメソッドはスーパークラスのメソッドを隠します。
静的メソッドの非表示とインスタンスメソッドの上書きの違いは、重要な意味を持っています。
あなたの例に戻って:
Animal myAnimal = myCat;
/* invokes static method on Animal, expected. */
Animal.testClassMethod();
/* invokes child class instance method (non-static - it's overriding) */
myAnimal.testInstanceMethod();
上記のステートメントはまだ非表示を示していません。
次に、以下のようにコードを変更して、異なる出力を取得します。
Animal myAnimal = myCat;
/* Even though myAnimal is Cat, Animal class method is invoked instead of Cat method*/
myAnimal.testClassMethod();
/* invokes child class instance method (non-static - it's overriding) */
myAnimal.testInstanceMethod();
上記の例に加えて、非表示と上書きの違いを明確にするための小さなサンプルコードを次に示します。
_public class Parent {
// to be hidden (static)
public static String toBeHidden() {
return "Parent";
}
// to be overridden (non-static)
public String toBeOverridden() {
return "Parent";
}
public void printParent() {
System.out.println("to be hidden: " + toBeHidden());
System.out.println("to be overridden: " + toBeOverridden());
}
}
public class Child extends Parent {
public static String toBeHidden() {
return "Child";
}
public String toBeOverridden() {
return "Child";
}
public void printChild() {
System.out.println("to be hidden: " + toBeHidden());
System.out.println("to be overridden: " + toBeOverridden());
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.printParent();
child.printChild();
}
}
_
child.printParent()
の呼び出しは以下を出力します:
非表示:親
オーバーライドされる:子供
child.printChild()
の呼び出しは以下を出力します:
非表示:Child
オーバーライドされる:子供
上記の出力(特に太字の出力)からわかるように、メソッドの非表示はオーバーライドとは異なる動作をします。
Javaでは、メソッドに対してのみ非表示とオーバーライドの両方が許可されます。同じ規則は変数には適用されません。変数のオーバーライドは許可されていないため、変数は非表示にしかできません(静的変数と非静的変数の違いはありません)。以下の例は、メソッドgetName()
がオーバーライドされ、変数name
が非表示になる方法を示しています。
_public class Main {
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.name); // prints Parent (since hiding)
System.out.println(p.getName()); // prints Child (since overriding)
}
}
class Parent {
String name = "Parent";
String getName() {
return name;
}
}
class Child extends Parent {
String name = "Child";
String getName() {
return name;
}
}
_
public class Parent {
public static void show(){
System.out.println("Parent");
}
}
public class Child extends Parent{
public static void show(){
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent parent=new Child();
parent.show(); // it will call parent show method
}
}
// We can call static method by reference ( as shown above) or by using class name (Parent.show())