web-dev-qa-db-ja.com

スーパースーパークラスメソッドの呼び出し

A、B、Cの3つのクラスがあるとします。

  • BはAを拡張します
  • CはBを拡張します

すべてにpublic void foo()メソッドが定義されています。

ここで、Cのfoo()メソッドから、Aのfoo()メソッドを呼び出します(その親Bのメソッドではなく、スーパースーパークラスAのメソッド)。

super.super.foo();を試しましたが、構文が無効です。どうすればこれを達成できますか?

36
Harish

リフレクションは使用できません。何かのようなもの

_Class superSuperClass = this.getClass().getSuperclass().getSuperclass();
superSuperClass.getMethod("foo").invoke(this);
_

superSuperClassでfoo-Methodを呼び出しても、invokeで「this」を指定すると、C.foo()が引き続き使用されるため、InvocationTargetExceptionになります。これは、すべてのJavaメソッドが仮想メソッドであるという事実からの結果です。

Bクラスからの助けが必要なようです(superFoo(){ super.foo(); }メソッドを定義するなど)。

とはいえ、このようなことを試してみると、設計上の問題のように見えるので、背景を教えておくと役立ちます。なぜこれを行う必要があるのですか?

32
Landei

できません-カプセル化が壊れるからです。

ownクラスのカプセル化が壊れていることがわかっていると想定されているため、スーパークラスのメソッドを呼び出すことができ、それを回避します...しかし、スーパークラスが適用しているルールがわからないため、そこで実装をバイパスすることはできません。

20
Jon Skeet

簡単な方法ではできません。

これはあなたができると思うことです:

クラスBにブール値を設定します。次に、Cから[super foo]のようにBのfooを呼び出す必要がありますが、これを行う前にブール値をtrueに設定します。 Bのfooでboolがtrueかどうかを確認し、その中でステップを実行せず、Aのfooを呼び出すだけです。

お役に立てれば。

3

はい、できます。これはハックです。このようにプログラムを設計しないようにしてください。

class A
{
    public void method()
    { /* Code specific to A */ }
}

class B extends A
{
    @Override
    public void method()
    { 
       //compares if the calling object is of type C, if yes Push the call to the A's method.
       if(this.getClass().getName().compareTo("C")==0)
       { 
           super.method(); 
       }
       else{  /*Code specific to B*/  }

    }
}

class C extends B
{
    @Override
    public void method()
    { 
        /* I want to use the code specific to A without using B */ 
        super.method();

    }
}
2
Jainam MJ

以前の回答を引用するには、「できません-カプセル化が壊れるからです。」それに追加したいのは:

ただし、メソッドがstaticpublicまたはprotected)の場合は、可能です。 静的メソッドを上書きする はできません。

public staticメソッドは、実際にこれを実行できることを証明するのは簡単です。

ただし、protectedの場合は、継承パス内のスーパークラスへのキャストを実行するために、メソッドの1つからそのスーパークラスメソッドが呼び出されます。

これは私が私の答えで探究しているコーナーケースです:

public class A {
    static protected callMe(){
        System.out.println("A");
    }
}

public class B extends A {
    static protected callMe(){
        System.out.println("B");
    }
}

public class C extends B {
    static protected callMe(){
        System.out.println("C");
        C.callMe();
    }

    public void accessMyParents(){
        A a = (A) this;
        a.callMe(); //calling beyond super class
    }
}

答えは「いいえ」のままですが、できる限りのケースを示したいだけですが、それはおそらく意味をなさず、単なる演習です。

2
Radu Ionescu

リフレクションAPIを使用する前に、そのコストについて検討してください。

簡単です。例えば:

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

0
user2490562

私の単純なケースでは、BとCの等しいメソッドをカプセル化する抽象クラスからBとCを継承する必要がありました。

_     A
     |
   Abstr
    / \
   B   C
_

それは問題を解決しませんが、CがBに類似している場合、単純なケースで使用できます。たとえば、Cが初期化されているが、Bの初期化子を使用したくない場合。その後、単純にAbstrメソッドを呼び出します。

これは、BとCの共通部分です。

_public abstract class Abstr extends AppCompatActivity {
    public void showProgress() {
    }

    public void hideProgress() {
    }
}
_

これはBであり、AppCompatActivityに存在する独自のメソッドonCreate()を持っています。

_public class B extends Abstr {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // Call from AppCompatActivity.
        setContentView(R.layout.activity_B); // B shows "activity_B" resource.
        showProgress();
    }
}
_

Cは独自のレイアウトを示しています。

_public class C extends Abstr {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // Call from AppCompatActivity.
        setContentView(R.layout.activity_C); // C shows "activity_C" resource.
        showProgress();
    }
}
_
0
CoolMind

私の同様の問題を解決した回避策があります:

クラスA、B、およびCのシナリオを使用すると、カプセル化を解除せず、クラスB内でクラスCを宣言する必要もないメソッドがあります。回避策は、クラスBのメソッドを別の保護されたメソッドに移動することです。

次に、それらのクラスBのメソッドが必要ない場合は、単にそのメソッドをオーバーライドしますが、そのメソッド内で「スーパー」を使用しないでください。オーバーライドして何もしないと、そのクラスBメソッドは効果的に無効になります。

public class A {
    protected void callMe() {
        System.out.println("callMe for A");
    }
}

public class B extends A {
    protected void callMe() {
        super.callMe();
        methodsForB(); // Class B methods moved out and into it's own method
    }

    protected void methodsForB() {
        System.out.println("methods for B");
    }
}

public class C extends B {

    public static void main(String[] args) {
        new C().callMe();
    }

    protected void callMe() {
        super.callMe();
        System.out.println("callMe for C");
    }

    protected void methodsForB() {
        // Do nothing thereby neutralising class B methods 
    }
}

結果は次のようになります。

callMe for A
callMe for C
0
Ray2U

スーパークラスがオーバーライドされたトップクラスのメソッドを呼び出すという問題がありました。これは私の回避策でした...

//これはa1()がスーパークラスのメソッドを呼び出して失敗するため、トップクラスのメソッドを呼び出すことができません

class foo1{
 public void a1(){
  a2();
  }
 public void a2(){}
 }
class foo2 extends foo1{
 {
 public void a1(){
//some other stuff
 super.a1();
 }
 public void a2(){
//some other stuff
 super.a2();
 }

//これにより、適切なスーパークラスメソッドの呼び出しが保証されます//パブリックメソッドはプライベートメソッドのみを呼び出すため、スーパークラスの機能に影響を与えることなく、すべてのパブリックメソッドをオーバーライドできます。

class foo1{
 public void a1(){
  a3();}
 public void a2(){
  a3();}
 private void a3(){
//super class routine
 }
class foo2 extends foo1{
 {
 public void a1(){
//some other stuff
 super.a1();
 }
 public void a2(){
//some other stuff
 super.a2();
 }

これがお役に立てば幸いです。 :)

0
Penny

それは不可能であり、スーパークラスの実装のみを呼び出すことに制限されています。

0
Andreas_D

匂い ここで何か怪しい。

「できるはずだからといって」、限界を超えているのではないでしょうか。これがあなたが得ることができる最高のデザインパターンであると確信していますか?それをリファクタリングしてみましたか?

0
lorenzog