调用超超类方法

Posted

技术标签:

【中文标题】调用超超类方法【英文标题】:Calling super super class method 【发布时间】:2011-03-28 05:41:51 【问题描述】:

假设我有 A、B 和 C 三个班级。

B 扩展 A C 扩展 B

都定义了public void foo() 方法。

现在我想从 C 的 foo() 方法调用 A 的 foo() 方法(不是它的父 B 的方法,而是超超类 A 的方法)。

我试过super.super.foo();,但它的语法无效。 我怎样才能做到这一点?

【问题讨论】:

只是因为有人应该问,如果你似乎希望它直接扩展 A,为什么 C 扩展 B? (我想还有其他部分你依赖 B 的功能,或者什么) 只有当 B 不覆盖 A 的 foo() 时,才能使用 super.foo() 从 C 调用 A 的 foo()。 Effective Java 第 2 版,第 16 条:优先组合优于继承。另外,查看装饰器模式;它可能更适合您的需要。 Why is super.super.method(); not allowed in Java? 的可能重复项 【参考方案1】:

这是不可能的,我们仅限于调用超类的实现。

【讨论】:

(Aarons 现在删除的答案给我留下了深刻的印象,我在发布后两秒钟就删除了我的答案;)-即使现在有更好的答案也没有删除它)【参考方案2】:

你不能 - 因为它会破坏封装。

你可以调用你的超类的方法,因为它假定你知道是什么破坏了你的自己的类中的封装,并且避免了这种情况......但是你不知道你的超类是什么规则强制 - 所以你不能只是绕过那里的实现。

【讨论】:

奇怪.. 我记得有一种方法可以调用 Object.toString() 即使 toString() 被超类重载。但是Type.this 无法编译。 :-/ @Aaron:我不确定 Object.toString() - 你在想System.identityHashCode吗? 没用过,我很确定是toString()。 Object.super.toString() 也不起作用。可能是早期 Java 版本中的错误或其他问题。【参考方案3】:

你不能以简单的方式做到这一点。

这是我认为你可以做的:

在你的 B 类中有一个 bool。现在你必须像 [super foo] 一样从 C 调用 B 的 foo,但在这样做之前将 bool 设置为 true。现在在 B 的 foo 中检查 bool 是否为真,然后不要执行任何步骤,只需调用 A 的 foo。

希望这会有所帮助。

【讨论】:

这是有史以来最疯狂的 Java hack :) Jon Skeet 的回答是最好的:不应启用这种功能,因为它会破坏封装。与其解决 HOW 来做这样的事情,我们应该解决 WHY 是否有人甚至想要做这样的事情(并撕毁那个违反 OOP 原则的论点) . @Nikita Rybak:人们要求不应该被要求的东西。因此,不应该给出的答案;) @polygenelubricants :我不是在挑战 Skeet 的回答。我也不敢。我只是给出了我的观点。 这似乎是线程不安全的。如果你要做这样的事情,你可以把 void foo (boolean flag) if (flag) super 。富 ( ) ;否则这个。富 ( ) ; 在 B 类中。至于封装,一旦你想出了为什么 B 类应该有一个 foo(boolean) 方法的原因,那么它是如何实现的就是一个实现细节。【参考方案4】:

你甚至不能使用反射。类似的东西

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

会导致InvocationTargetException,因为即使你在superSuperClass上调用foo-Method,当你在invoke中指定“this”时它仍然会使用C.foo()。这是因为所有 Java 方法都是虚拟方法这一事实。

您似乎需要 B 类的帮助(例如,通过定义 superFoo() super.foo(); 方法)。

也就是说,如果你尝试这样的事情,它看起来像是一个设计问题,所以给我们一些背景知识会很有帮助:为什么你需要这样做?

【讨论】:

这不是我的要求。我只是在脑海中想出所有可能的继承权并得到了这个问题。谢谢..我得到了答案 好吧,我需要这样做,因为超类以我不想要的方式覆盖了超超类......而且它是一个库......所以我想从 super 调用它超类...也许获取超超类的方法的源代码并再次覆盖可以解决这个问题? 当然你可以在你的方法中写你想要的,所以在这种情况下复制代码应该可以工作。但是,如果超超类在foo() 中使用私有或封装私有字段或方法,则需要反射来访问这些(当然,如果有安全管理器,您将不走运)。此外,如果原来的foo() 代码让super 自己调用,它也不会工作。【参考方案5】:

我smell这里有些可疑。

您确定您不只是“仅仅因为您应该能够做到”而将信封推得太远吗?你确定这是你能得到的最好的设计模式吗?您是否尝试过重构它?

【讨论】:

【参考方案6】:

我遇到了一个问题,超类会调用被覆盖的***方法。 这是我的解决方法...

//调用超类方法将失败,因为 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();
 

我希望这会有所帮助。 :)

【讨论】:

这实际上并没有回答问题。请重新阅读问题。【参考方案7】:

在使用反射 API 之前,请考虑一下它的成本。

这很容易做到。例如:

B 的 C 子类和 A 的 B 子类。这三个都有方法 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类

【讨论】:

【参考方案8】:

引用之前的回答“你不能 - 因为它会破坏封装。”我想补充一点:

但是有一个特殊情况可以,即方法是staticpublicprotected)。你不能overwrite the static method。

拥有public static 方法很容易证明您确实可以做到这一点。

但是,对于protected,您需要从您的一个方法内部对继承路径中的任何超类执行强制转换,并且该超类方法将被调用。

这是我在回答中探索的极端案例:

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
    

答案仍然是“否”,只是想展示一个你可以做到的案例,尽管它可能没有任何意义,只是一个练习。

【讨论】:

【参考方案9】:

是的,你可以做到。这是一个黑客。尽量不要这样设计你的程序。

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

    

【讨论】:

这叫做控制反转!【参考方案10】:

在我的简单情况下,我必须从抽象类继承 B 和 C,它封装了 B 和 C 的相等方法。所以

     A
     |
   Abstr
    / \
   B   C

虽然不能解决问题,但它可以用在简单的情况下,当 C 与 B 相似时。例如,当 C 被初始化,但不想使用 B 的初始化器时。然后它只是调用抽象方法。

这是B和C的共同部分:

public abstract class Abstr extends AppCompatActivity 
    public void showProgress() 
    

    public void hideProgress() 
    

这是B,有自己的方法onCreate(),存在于AppCompatActivity

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

【讨论】:

【参考方案11】:

有一个解决方法可以解决我的类似问题:

使用 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

【讨论】:

【参考方案12】:

这不是您通常应该做的事情,但是在特殊情况下,您必须解决第三方库中的某些错误(如果允许这样做),您可以实现调用已经存在的超超类方法使用delegation pattern 和一个扩展超超类以用作桥梁的内部类被覆盖:

class A() 
  public void foo() 
    System.out.println("calling A");
  


class B extends A() 
  @Overwrite
  public void foo() 
    System.out.println("calling B");
  


class C extends B() 
  private final a;

  public C() 
    this.a = new AExtension();
  

  @Overwrite
  public void foo() 
    a.foo();
  

  private class AExtension extends A 
  


This way you will be able to not only call the super super method but also combine calls to other super super class methods with calls to methods of the super class or the class itself by using `C.super` or `C.this`. 

【讨论】:

以上是关于调用超超类方法的主要内容,如果未能解决你的问题,请参考以下文章

从超类调用子类的方法

使用“this”调用超类方法的子类

我可以模拟超类方法调用吗?

在 ExtJS 中调用超类方法的更好方法

在Objective C中调用超类方法的时间是不是重要?

调用被覆盖的方法,超类调用被覆盖的方法