なぜsuper.super.method(); Javaでは許可されていませんか?


360

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

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

それが多くの場合に役立つかどうかはわかりませんが、なぜそれが役に立たないのか、そしてこのようなものが他の言語に存在するのかどうか疑問に思います。

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

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


7
super.super.toString()クラスを拡張することを選択して、その機能のすべて(一部ではない)を受け入れる場合、呼び出しを希望することは、あなた自身の決定と矛盾します。
DayaMoon 2014

回答:


484

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

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);
    }
}

これで、好きなものを追加できます。 RedItemsが壊れています。

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


38
良い例です。しかし、基本クラスがアイテムを受け入れるときの設計は悪いと思いましたが、派生クラスはアイテムを拒否します。派生クラスは基本クラスのドロップイン置換として使用できないためです(置換原則の違反)。その正しい考えですか、そのような階層はきれいですか?
ヨハネスシャウブ-litb 2009

5
継承よりも構成を意味しますか?この例は継承を避ける理由ではありません-他にもたくさんありますが。
Jon Skeet、

3
@Tim:これは、カプセル化違反のわかりやすい例にすぎません。代わりにプロパティを設定している可能性があります。さらに、すべての側面がタイプレベルで表示されるわけではありません。ジェネリックスはすべてに対する答えではありません。
Jon Skeet、

12
@ JohannesSchaub-litbアイテムのコントラクトをコーディングしてRedItemsのインスタンスを使用すると予期しないNotRedItemExceptionが発生するため、これはリスコフの置換原則に違反していると思います。サブクラスは入力のスーパーセットを受け取り、出力のサブセットを返す必要があると常に教えられていました。つまり、サブクラスは、スーパークラスに有効な入力を拒否したり、スーパークラスが生成できない出力を生成したりしてはなりません。これには、スーパークラスがスローしないエラーのスローが含まれます。
Konstantin Tarashchanskiy

4
@piechuckerr:時には本、時にはブログの投稿、時にはただの経験...
Jon Skeet

72

ジョン・スキートが正解だと思います。キャストすることにより、スーパークラスのスーパークラスからシャドウ変数にアクセスできることを追加したいと思います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
super.x = 2
((T2)this).x = 2
((T1)this).x = 1
((I)this).x = 0

JLSの例)

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


6
スーパークラスの変数と同じ名前の変数があり、何らかの理由でその変数を変更できない(または変更できない)場合でも、同じ名前でスーパークラスの変数にアクセスできます。用途は何ですか、あなたは尋ねますか?ええと...私はそれを使ったことがありません。
マイケルマイヤーズ

エクスプレッションドキュメントへのリンク。OCPJPのために勉強している人々のためのいくつかの素晴らしい例。
Gordon

驚くばかり。キャストを使うなんて考えたこともなかったでしょう。
Thomas Eding、2011年

クラスT1が変数xをオーバーライドする理由 デフォルトでその静的なファイナル?
amarnath harish

@amarnathharish:オーバーライドされず、フィールドはデフォルトで非最終的なパッケージ保護されています。
マイケル・マイヤーズ

41

次のコードでは、ほとんどの場合、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);
    }
}

10
リフレクションを使用すると、何でもできます。はい:)文字列を変更可能にすることもできます。
BalusC 2010年

23
なんて恐ろしいことでしょう!私はあなたにそれを行う方法を理解するためだけに+1を与えています:)
ラリーワタナベ

6
それは素晴らしいトリックですが、それでも常に必要な(ただし必要な)super.superを呼び出すことと同等ではありません。これは、super.super呼び出しがC(C + B + A)のコンテキストを運ぶのに対し、答えはインスタンスを作成するためですたとえば、すべてのdoThatがgetContext()を呼び出し、getContextがクラスごとに異なる方法で実装されている場合、この回答は機能しません。答えでは、AのgetContext()を使用しますが、使用できないsuper.superを呼び出すと、CのgetContextが使用されます。
2012年

うーん。場合によっては、動的プロキシ(javahowto.blogspot.co.uk/2011/12/…)を使用してinorの異論を克服し、メソッド呼び出しを元のオブジェクトにリダイレクトする(各呼び出しの後に変数を同期する)ことができるでしょう。ただし、プロキシはすべてがインターフェースを実装する必要があるようです。また、私は....スーパースーパークラスは具体的には、その超スーパーのいずれかの方法で呼び出すことが、あなたがそれらをリダイレクトする必要はありませんしたいことは可能ですかどうか疑問
Erhannis

別のコメントで、ブロッキングsuper.super.はプログラマが回避策を追求するために足を踏み入れて自分を撃つための新しい複雑な悪質な方法を見つけるように誘うと述べましたが、これはその完璧な例です。このように彼らは個人的にそして文字通りあなたを足で撃ちます。+1
ブレーデンベスト

11

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

Jon Skeetは、美しい例を挙げて、優れた回答を示しています。Matt Bにはポイントがあります。すべてのスーパークラスにスーパーがあるわけではありません。スーパーがないスーパーのスーパーを呼び出すと、コードが壊れます。

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

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


3
「すべてのスーパークラスにスーパーがあるわけではない」と言います。ええと、java.lang.Object以外はすべて、「null」になる可能性があります。つまり、ほとんどすべてにスーパーがあります。
TimBüthe2009

アプリケーションプログラマは書き込みすべてのクラスは(。java.lang.Object上位にはないが、アプリケーションプログラマがそれを書き込みません)「スーパー」を持っている
finnw

2
スーパーが多すぎる場合、super.super.super ... superをコンパイル時エラーにすることで簡単に解決できます。Javaにはパブリック継承しかないため、誰かが継承階層を変更した場合は、インターフェースが変更されます。ですから、super ^ nが怖いのではないかと心配する必要はありません。
Thomas Eding、2011年

7

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

他のポスターで示されているように、これは反射によって行うことができますが、次のようなことを行うことができるはずです

(SuperSuperClass this).theMethod();

私は今この問題を扱っています-迅速な修正は、スーパークラスのメソッドをコピーしてサブサブクラスのメソッドに貼り付けることです:)


1
メソッドを呼び出す前にキャストしても、委譲されるメソッドは変更されません。使用されるのは常にサブクラスの実装であり、スーパーの実装ではありません。たとえば、stackoverflow.com / questions / 1677993 /…を
Joshua Goldberg

1
@Larryこれはまさに私がいた状況であり、まさに私が使用した修正です。いいね!
bcr 2012

必要なメソッドのコードがある場合、必要なすべてのフィールドを開き、このコードをサブクラスで実行できます。
Enyby 2015

6

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

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

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


4
明らかに、これはコンパイラエラーになります-コンパイラが持っている情報です。
マイケルボルグワート

4

メソッドを上書きし、そのすべてのスーパークラスバージョン(たとえば、equals)を使用したい場合、実質的に常に直接スーパークラスバージョンを最初に呼び出したいと思います。 。

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

this->ReallyTheBase::foo();

6
誰かが1つのメソッドが正しく実装されていないサブクラスを書いたが、スーパークラスのメソッドが作業の90%を実行する場合は理にかなっています。次に、サブクラスを作成し、スーパークラススーパークラスメソッドを呼び出すメソッドをオーバーライドして、独自の10%を追加します。
Larry Watanabe、

3

一見すると、あまり使われていないので。私がそれを使用しているのを見ることができる唯一の理由は、あなたの直接の親がいくつかの機能をオーバーライドしていて、あなたがそれをオリジナルに復元しようとしている場合です。

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


3

この Githubプロジェクト、特にobjectHandle変数を見てください。このプロジェクトでは、孫の祖父母メソッドを実際に正確に呼び出す方法を示します。

リンクが壊れた場合に備えて、ここにコードがあります:

import lombok.val;
import org.junit.Assert;
import org.junit.Test;

import java.lang.invoke.*;

/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
    private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
        val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        return (MethodHandles.Lookup) field.get(null);
    }

    @Test
    public void test() throws Throwable {
        val lookup = getImplLookup();
        val baseHandle = lookup.findSpecial(Base.class, "toString",
            MethodType.methodType(String.class),
            Sub.class);
        val objectHandle = lookup.findSpecial(Object.class, "toString",
            MethodType.methodType(String.class),
            // Must use Base.class here for this reference to call Object's toString
            Base.class);
        val sub = new Sub();
        Assert.assertEquals("Sub", sub.toString());
        Assert.assertEquals("Base", baseHandle.invoke(sub));
        Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
    }

    private static String toString(Object o) {
        return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
    }

    public class Sub extends Base {
        @Override
        public String toString() {
            return "Sub";
        }
    }

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

ハッピーコーディング!!!!


あなたの科学者は、できるかどうかに夢中になっていたので、そうするべきかどうか考えるのをやめませんでした。実際にこれを実行しないでください...:P 🤔
ティム・ブーテ

ええ、それらは私のものではなく、コーダーの言葉です。私の意見では、いつか実際にそれが必要になるかもしれません
kyay

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();

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


私はSuperSuperClassとChildClassを所有しているがSuperClassは所有していない状況にいることに気付いたので、最初の解決策が有用であることがわかりました。
xofon 2016年


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);

     }
}

実行:A BUILD SUCCESSFUL(合計時間:0秒)


1
わかりましたが、あなたは新しいインスタンスを作成しているので、オブジェクトに状態がある場合、これは機能しません。
TimBüthe、2011

1

基本クラスのコードを変更できない場合、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から実際のクラスの前にこのクラスをロードする必要があります。次に、メソッドまたはコンストラクターを書き換えます。

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


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セント...


1

@ジョンスキートニース説明。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を実現するための理想的または良い習慣であるかどうかはわかりませんが、それでも機能しています。


1
まあ、それは創造的ですが、私の質問に実際には答えません。Cはまだsuper.superを呼び出さず、Bは異なる動作をします。AとBを変更できる場合は、instanceofを使用する代わりに別のメソッドを追加できます。Super.super.fooは、AとBにアクセスできず、それらを変更できない場合に役立ちます。
TimBüthe2013

@TimButheに同意しますが、super.superを呼び出したい場合は、意図的に親クラスの動作を無視したいので、Javaの既存の構文でそのことを達成する必要があります。(任意のオプションで、instanceof /別の方法のいずれかを希望)
Sanjay Jain

0

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

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

2
静的メソッドを使用しているので、あなたの例はちょっと...かなり悪いです。静的メソッドを使用する場合、変数やスーパーはまったく必要ありません。ここでいくつかの基本的なOOの概念を見逃しているかもしれません。必ず読んでください。申し訳ありませんが、反対票を投じる必要があります。
TimBüthe11年

1
静的メソッドがなくても簡単にできたでしょう。とても簡単です。これを行う方法の簡単な例を探していました。
Ashtheking

私はあなたの要点を得ます、フィールドにスーパー変数を格納することはこれを解決する1つの方法です。ただし、静的メソッドの場合、変数は不要であり、そのまま使用できます。次に、変数の1つで静的メソッドを呼び出すことはお勧めできません。ほとんどのIDEで警告が表示されます。答えを修正して、静的なものを削除すると、私は自分の反対票を削除することができます。問題はありません。
TimBüthe11年

あなたは近くにいますが、コードはコンパイルされません。メインメソッドの静的コンテキストからgetNumberにアクセスしようとしました。これを実際にコンパイルしようとしましたか?(そして、あなたのUltraFooはSuperFooを拡張すべきではありませんか?)
TimBüthe

私はあなたに残酷になりたくはありませんが、new UltraFoo.getNumber()括弧を逃したのでコンパイルしません。ただし、コードの概念がかなり明確になったので、私の投票を削除しました。ありがとうございます。
TimBüthe、2011

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


ああ、あなたはこのようなクラス階層を実装することを提唱していますか?残念ながら、ベイビー(娘のサブクラス)からママのメソッドにアクセスする場合、これは非常に厄介になり始めます...
bcr

ヤコフ・ファインは正しい。つまり、元の質問はsuper.superでオーバーライドされたメソッドを呼び出すことに関するものだったため、これは良い例ではありません。
2012年

0

これは相続協定を破る問題だと思います。
クラスを拡張することで、その動作、機能に従い、同意します
。を呼び出すときにsuper.super.method()、独自の従順契約を破る必要があります。

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

ただし、呼び出す必要があると感じる状況が発生する可能性があります。super.super.method()通常は、コード内または継承したコード内で設計記号が不適切です。
場合は、スーパースーパー、スーパークラスは(一部のレガシーコード)をリファクタリングすることはできませんし、相続上の構図を選びます。

カプセル化の解除とは、カプセル化されたコードを解除することによっていくつかのメソッドを@Overrideするときです。オーバーライドされないように設計されたメソッドはfinalとマークされ ます。


0

C#では、次のような祖先のメソッドを呼び出すことができます。

public class A
    internal virtual void foo()
...
public class B : A
    public new void foo()
...
public class C : B
    public new void foo() {
       (this as A).foo();
    }

また、Delphiでこれを行うことができます。

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

しかし、Javaでは、いくつかの機材によってのみそのような焦点を合わせることができます。1つの可能な方法は次のとおりです。

class A {               
   int y=10;            

   void foo(Class X) throws Exception {  
      if(X!=A.class)
         throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
      y++;
      System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
   }
   void foo() throws Exception { 
      System.out.printf("A.foo()\n");
      this.foo(this.getClass()); 
   }
}

class B extends A {     
   int y=20;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==B.class) { 
         y++; 
         System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }
}

class C extends B {     
   int y=30;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==C.class) { 
         y++; 
         System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }

   void DoIt() {
      try {
         System.out.printf("DoIt: foo():\n");
         foo();         
         Show();

         System.out.printf("DoIt: foo(B):\n");
         foo(B.class);  
         Show();

         System.out.printf("DoIt: foo(A):\n");
         foo(A.class);  
         Show();
      } catch(Exception e) {
         //...
      }
   }

   void Show() {
      System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
   }
} 

objC.DoIt()結果出力:

DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31

DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31

DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31

C#では、それは非仮想メソッドに対してのみ機能し、すべてのメソッドはJavaで仮想であるため、実際には違いはありません。
Agent_L 2018年

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


-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で印刷


2
この回答は質問の範囲外です。OPは、祖父母のクラスでメソッドを呼び出す方法を尋ねませんでした。問題は、super.super.method()Javaでが有効なコードではない理由の説明です。
Jed Schaaf、2016年

-1

キーワードsuperは、スーパークラスのメソッドを呼び出すための単なる方法です。Javaチュートリアル:https : //docs.oracle.com/javase/tutorial/java/IandI/super.html

メソッドがスーパークラスのメソッドの1つをオーバーライドする場合、キーワードsuperを使用してオーバーライドされたメソッドを呼び出すことができます。

それがスーパーオブジェクトのリファレンスであるとは思わないでください!!! いいえ、それはスーパークラスのメソッドを呼び出すための単なるキーワードです。

次に例を示します。

class Animal {
    public void doSth() {
        System.out.println(this);   // It's a Cat! Not an animal!
        System.out.println("Animal do sth.");
    }
}

class Cat extends Animal {
    public void doSth() {
        System.out.println(this);
        System.out.println("Cat do sth.");
        super.doSth();
    }
}

あなたが呼び出すとcat.doSth()、メソッドdoSth()クラスでは、Animal印刷されthis、それは猫です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.