403

私はこの質問を読んで、次のように書くことができれば、簡単に解決できると思いました(それなしでは解決できないわけではありません):

@Override
public String toString() {
    return super.super.toString();
}

多くの場合に役立つかどうかはわかりませんが、なぜそうでないのか、他の言語にこのようなものが存在するのだろうか.

皆さんはどう思いますか?

編集: 明確にするために:はい、Javaでは不可能であり、私はそれを見逃すことはありません。これは私が期待していた動作ではなく、コンパイル エラーが発生して驚きました。私はちょうどアイデアを持っていて、それについて話し合うのが好きです.

4

22 に答える 22

525

カプセル化に違反しています。親クラスの動作をバイパスすることはできません。自分のクラスの動作を (特に同じメソッド内から)バイパスできるが、親の動作はバイパスできない場合があるのは理にかなっています。たとえば、ベースの「アイテムのコレクション」、「赤いアイテムのコレクション」を表すサブクラス、および「大きな赤いアイテムのコレクション」を表すサブクラスがあるとします。それは理にかなっています:

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}

それは問題ありません - RedItems は、含まれるアイテムがすべて赤であることを常に確信できます。ここで、super.super.add() を呼び出すことができたとします。

public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}

これで、好きなものを追加できるようになり、 invariantRedItemsが壊れました。

それは理にかなっていますか?

于 2009-02-25T15:15:40.750 に答える
86

Jon Skeet が正しい答えを持っていると思います。キャストすることにより、スーパークラスのスーパークラスからシャドウ変数にアクセスできることを追加したいと思いますthis

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
        int x = 3;
        void test() {
                System.out.println("x=\t\t"          + x);
                System.out.println("super.x=\t\t"    + super.x);
                System.out.println("((T2)this).x=\t" + ((T2)this).x);
                System.out.println("((T1)this).x=\t" + ((T1)this).x);
                System.out.println("((I)this).x=\t"  + ((I)this).x);
        }
}

class Test {
        public static void main(String[] args) {
                new T3().test();
        }
}

出力を生成します:

x=3
スーパー.x= 2
((T2)これ).x=2
((T1)これ).x=1
((私)これ).x= 0

( JLSの例)

ただし、メソッド呼び出しはオブジェクトの実行時の型に基づいて決定されるため、これはメソッド呼び出しには機能しません。

于 2009-02-25T15:50:32.290 に答える
46

ほとんどの場合、次のコードで super.super...super.method() を使用できると思います。(それをするのは醜いとしても)

要するに

  1. 先祖型の一時インスタンスを作成する
  2. フィールドの値を元のオブジェクトから一時オブジェクトにコピーします
  3. 一時オブジェクトでターゲット メソッドを呼び出す
  4. 変更された値を元のオブジェクトにコピーします

使用法 :

public class A {
   public void doThat() { ... }
}

public class B extends A {
   public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
   public void doThat() {
      Magic.exec(A.class, this, "doThat");
   }
}


public class Magic {
    public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
            String methodOfParentToExec) {
        try {
            Type type = oneSuperType.newInstance();
            shareVars(oneSuperType, instance, type);
            oneSuperType.getMethod(methodOfParentToExec).invoke(type);
            shareVars(oneSuperType, type, instance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
            SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
        Class<?> loop = clazz;
        do {
            for (Field f : loop.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                f.set(target, f.get(source));
            }
            loop = loop.getSuperclass();
        } while (loop != Object.class);
    }
}
于 2010-04-22T15:54:45.193 に答える
12

コメントするのに十分な評判がないので、これを他の回答に追加します。

Jon Skeet は、美しい例を挙げて見事に答えています。Matt B の指摘は次のとおりです。すべてのスーパークラスにスーパーがあるわけではありません。スーパーを持たないスーパーのスーパーを呼び出すと、コードが壊れます。

オブジェクト指向プログラミング (これは Java です) は、関数ではなくオブジェクトに関するものです。タスク指向プログラミングが必要な場合は、C++ などを選択してください。オブジェクトがそのスーパー クラスに適合しない場合は、それを「祖父母クラス」に追加するか、新しいクラスを作成するか、適合する別のスーパーを見つける必要があります。

個人的には、この制限が Java の最大の強みの 1 つであることを発見しました。コードは、私が使用した他の言語に比べてやや厳格ですが、何が期待できるかは常にわかっています。これは、Java の「シンプルで親しみやすい」という目標を達成するのに役立ちます。私の考えでは、super.super の呼び出しは単純ではなく、慣れ親しんだものでもありません。開発者も同じ気持ちだったのではないでしょうか?

于 2009-02-25T15:50:03.930 に答える
7

これを行うには、いくつかの正当な理由があります。正しく実装されていないメソッドを持つサブクラスがある可能性がありますが、親メソッドは正しく実装されています。サードパーティのライブラリに属しているため、ソースを変更できない/変更したくない場合があります。この場合、サブクラスを作成しますが、1 つのメソッドをオーバーライドして super.super メソッドを呼び出します。

他のいくつかのポスターで示されているように、リフレクションを介してこれを行うことは可能ですが、次のようなことができるはずです

(SuperSuperClass this).theMethod();

私は今、この問題に取り組んでいます - 簡単な修正は、スーパークラス メソッドをサブサブクラス メソッドにコピー アンド ペーストすることです:)

于 2010-05-07T14:24:56.613 に答える
6

他の人が述べた非常に良い点に加えて、別の理由があると思います: スーパークラスにスーパークラスがない場合はどうなるでしょうか?

すべてのクラスは自然に (少なくとも) 拡張されるためObjectsuper.whatever()は常にスーパークラスのメソッドを参照します。しかし、クラスが拡張のみの場合はどうなるでしょうか? その場合、Object何をsuper.super参照するのでしょうか? その動作はどのように処理する必要がありますか? コンパイラ エラー、NullPointer など?

これが許可されない主な理由はカプセル化に違反していると思いますが、これも小さな理由かもしれません。

于 2009-02-25T15:34:04.293 に答える
4

メソッドを上書きし、そのすべてのスーパークラス バージョン ( for などequals) が必要な場合は、事実上、直接スーパークラス バージョンを最初に呼び出したいと思うでしょう。 .

メソッドの任意のスーパークラスのバージョンを呼び出すことは、めったに意味がないと思います(たとえあったとしても、そうするケースは考えられません)。Javaでそれが可能かどうかはわかりません。C++ で実行できます。

this->ReallyTheBase::foo();
于 2009-02-25T15:14:41.067 に答える
3

super.super.method() の呼び出しは、基本クラスのコードを変更できない場合に意味があります。これは、既存のライブラリを拡張しているときによく発生します。

まず、なぜそのクラスを拡張するのかを自問してください。答えが「変更できないため」である場合は、アプリケーションで正確なパッケージとクラスを作成し、いたずらなメソッドを書き直すか、デリゲートを作成できます。

package com.company.application;

public class OneYouWantExtend extends OneThatContainsDesiredMethod {

    // one way is to rewrite method() to call super.method() only or 
    // to doStuff() and then call super.method()

    public void method() {
        if (isDoStuff()) {
            // do stuff
        }
        super.method();
    }

    protected abstract boolean isDoStuff();


    // second way is to define methodDelegate() that will call hidden super.method()

    public void methodDelegate() {
        super.method();
    }
    ...
}

public class OneThatContainsDesiredMethod {

    public void method() {...}
    ...
}

たとえば、アプリケーションでorg.springframework.test.context.junit4.SpringJUnit4ClassRunnerクラスを作成して、このクラスを jar から実際のクラスの前にロードすることができます。次に、メソッドまたはコンストラクターを書き直します。

注意:これは絶対的なハックであり、使用することは強くお勧めしませんが、機能しています! クラスローダーに問題がある可能性があるため、このアプローチの使用は危険です。また、上書きされたクラスを含むライブラリを更新するたびに問題が発生する可能性があります。

于 2011-11-16T11:00:30.757 に答える
3

あまり使われていないので推測ですが。私がそれを使用する唯一の理由は、直接の親がいくつかの機能をオーバーライドし、それを元に戻そうとしている場合です。

クラスの直接の親は、祖父母よりもクラスと密接に関連している必要があるため、これはオブジェクト指向の原則に反しているように思えます。

于 2009-02-25T15:16:12.903 に答える
3

@Jon Skeetいい説明。IMO super.super メソッドを呼び出したい場合は、直接の親の動作を無視したいが、祖父母の動作にアクセスしたい必要があります。これは、インスタンス Of を通じて実現できます。以下のコードのように

public class A {
    protected void printClass() {
        System.out.println("In A Class");
    }
}

public class B extends A {

    @Override
    protected void printClass() {
        if (!(this instanceof C)) {
            System.out.println("In B Class");
        }
        super.printClass();
    }
}

public class C extends B {
    @Override
    protected void printClass() {
        System.out.println("In C Class");
        super.printClass();
    }
}

これがドライバークラスです。

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

これの出力は次のようになります

In C Class
In A Class

この場合、クラス B の printClass の動作は無視されます。これがsuper.superを達成するための理想的または適切な方法であるかどうかはわかりませんが、それでも機能しています。

于 2013-09-04T06:18:10.383 に答える
2

可能であれば、super.super メソッド本体を別のメソッドに配置します

class SuperSuperClass {
    public String toString() {
        return DescribeMe();
    }

    protected String DescribeMe() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    public String toString() {
        return DescribeMe();
    }
}

または、スーパースーパークラスを変更できない場合は、これを試すことができます:

class SuperSuperClass {
    public String toString() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return DescribeMe(super.toString());
    }

    protected String DescribeMe(string fromSuper) {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    protected String DescribeMe(string fromSuper) {
        return fromSuper;
    }
}

どちらの場合も、

new ChildClass().toString();

「私はスーパースーパーです」の結果

于 2013-10-29T10:28:18.377 に答える
1
public class A {

     @Override
     public String toString() {
          return "A";
     }

}


public class B extends A {

     @Override
     public String toString() {
          return "B";
     }

}

public class C extends B {

     @Override
     public String toString() {
          return "C";
     }

}


public class D extends C {

     @Override
     public String toString() {
          String result = "";
          try {
                result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
          } catch (InstantiationException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          }
          return result;
     }

}

public class Main {

     public static void main(String... args) {
          D d = new D();
          System.out.println(d);

     }
}

実行:ビルド成功(合計時間:0秒)

于 2011-07-20T11:48:36.283 に答える
1

アーキテクチャが、いくつかの派生クラスに代わって実装される共通のCustomBaseClassで共通の機能を構築する場合、このような状況が発生しました。ただし、特定の派生クラスの特定のメソッドの共通論理を回避する必要があります。このような場合、super.super.methodX実装を使用する必要があります。

これを実現するには、CustomBaseClassにブール値のメンバーを導入します。これを使用して、カスタム実装を選択的に延期し、必要に応じてデフォルトのフレームワーク実装に譲ることができます。

        ...
        FrameworkBaseClass (....) extends...
        {
           methodA(...){...}
           methodB(...){...}
        ...
           methodX(...)
        ...
           methodN(...){...}

        }
        /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
        CustomBaseClass(...) extends FrameworkBaseClass 
        {
        private boolean skipMethodX=false; 
        /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/

           methodA(...){...}
           methodB(...){...}
        ...
           methodN(...){...}

           methodX(...){
                  if (isSkipMethodX()) {
                       setSKipMethodX(false);
                       super.methodX(...);
                       return;
                       }
                   ... //common method logic
            }
        }

        DerivedClass1(...) extends CustomBaseClass
        DerivedClass2(...) extends CustomBaseClass 
        ...
        DerivedClassN(...) extends CustomBaseClass...

        DerivedClassX(...) extends CustomBaseClass...
        {
           methodX(...){
                  super.setSKipMethodX(true);
                  super.methodX(...);
                       }
        }

ただし、フレームワークとアプリで優れたアーキテクチャの原則に従うことで、isAアプローチの代わりにhasAアプローチを使用することで、このような状況を簡単に回避できます。しかし、常に適切に設計されたアーキテクチャを期待することはあまり実用的ではないため、堅実な設計原則から離れて、このようなハックを導入する必要があります。ちょうど私の2セント...

于 2013-02-26T19:26:09.373 に答える
1

リフレクションを使用して、少なくともそのスーパークラスのインスタンスを取得する必要はありませんが、少なくともそのスーパークラスのクラスを取得することは可能であるように思われます。これが役立つ場合は、http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()の Javadoc を検討してください。

于 2009-07-02T20:22:11.997 に答える
0

これは相続協定を破る問題だと思います。
クラスを拡張することにより、その動作に従う/同意することが
できsuper.super.method()ます。

スーパークラスからチェリーピックすることはできません

ただし、呼び出す必要があると感じる状況が発生する場合があります。super.super.method()通常は、コード内または継承するコード内で、悪い設計の兆候です。スーパークラスとスーパー スーパー
クラスをリファクタリングできない 場合(一部のレガシー コード)、継承よりも構成を選択します。 カプセル化の解除は、カプセル化されたコードを解除してメソッドを@Overrideする場合です。オーバーライドしないように設計されたメソッドは finalとマークされます。

于 2014-04-09T07:55:15.097 に答える
0

やり方は簡単です。例えば:

B の C サブクラスと A の B サブクラス。たとえば、3 つの両方にメソッド methodName() があります。

public abstract class A {

    public void methodName() {
        System.out.println("Class A");
    }

}

public class B extends A {

    public void methodName() {
        super.methodName();
        System.out.println("Class B");
    }

    // Will call the super methodName
    public void hackSuper() {
        super.methodName();
    }

}

public class C extends B {

    public static void main(String[] args) {
        A a = new C();
        a.methodName();
    }

    @Override
    public void methodName() {
        /*super.methodName();*/
        hackSuper();
        System.out.println("Class C");
    }

}

クラス C の実行 出力は次のようになります: クラス A クラス C

出力の代わり: クラス A クラス B クラス C

于 2015-02-16T12:08:56.273 に答える
0

IMO、これsuper.super.sayYourName()はJavaで動作を実現するためのクリーンな方法です。

public class GrandMa {  
    public void sayYourName(){  
        System.out.println("Grandma Fedora");  
    }  
}  

public class Mama extends GrandMa {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName();  
        }else {  
            System.out.println("Mama Stephanida");  
        }  
    }  
}  

public class Daughter extends Mama {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName(lie);  
        }else {  
            System.out.println("Little girl Masha");  
        }  
    }  
}  

public class TestDaughter {
    public static void main(String[] args){
        Daughter d = new Daughter();

        System.out.print("Request to lie: d.sayYourName(true) returns ");
        d.sayYourName(true);
        System.out.print("Request not to lie: d.sayYourName(false) returns ");
        d.sayYourName(false);
    }
}

出力:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha

于 2011-03-27T18:35:12.873 に答える
-1

スーパークラスが必要になると思われる場合は、そのクラスの変数でスーパークラスを参照できます。例えば:

public class Foo
{
  public int getNumber()
  {
    return 0;
  }
}

public class SuperFoo extends Foo
{
  public static Foo superClass = new Foo();
  public int getNumber()
  {
    return 1;
  }
}

public class UltraFoo extends Foo
{
  public static void main(String[] args)
  {
    System.out.println(new UltraFoo.getNumber());
    System.out.println(new SuperFoo().getNumber());
    System.out.println(new SuperFoo().superClass.getNumber());
  }
  public int getNumber()
  {
    return 2;
  }
}

印刷する必要があります:

2
1
0
于 2011-06-04T19:03:35.913 に答える
-1
public class SubSubClass extends SubClass {

    @Override
    public void print() {
        super.superPrint();
    }

    public static void main(String[] args) {
        new SubSubClass().print();
    }
}

class SuperClass {

    public void print() {
        System.out.println("Printed in the GrandDad");
    }
}

class SubClass extends SuperClass {

    public void superPrint() {
        super.print();
    }
}

出力: GrandDad で印刷

于 2015-04-17T05:34:34.383 に答える