【问题标题】:Why is super.super.method(); not allowed in Java?为什么是 super.super.method();在 Java 中不允许?
【发布时间】:2010-10-09 20:07:39
【问题描述】:

我读了this question 并认为如果有人可以写,这将很容易解决(并不是说没有它就无法解决):

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

我不确定它在很多情况下是否有用,但我想知道为什么它没有用,以及其他语言中是否存在类似的东西。

你们觉得呢?

编辑: 澄清一下:是的,我知道,这在 Java 中是不可能的,我并没有真正想念它。这不是我期望的工作,并且很惊讶得到编译器错误。我刚刚有了这个想法,想讨论一下。

【问题讨论】:

  • 想要调用super.super.toString() 与您自己的决定相矛盾,因为您选择扩展一个类从而接受其所有(不是部分)功能。

标签: java superclass


【解决方案1】:

我认为,如果您覆盖一个方法并想要它的所有超类版本(例如,equals),那么您实际上总是希望首先调用直接超类版本,哪个会调用它的超类如果需要,版本依次进行。

我认为调用某个方法的任意超类版本几乎没有意义(如果有的话。我想不出有什么情况)。我不知道这在 Java 中是否可行。可以在 C++ 中完成:

this->ReallyTheBase::foo();

【讨论】:

  • 如果有人编写了一个子类,其中一个方法实现不正确,但超类方法完成了 90% 的工作,这是有道理的。然后你要创建一个子类,并且重写调用超类超类方法的方法,并添加你自己的10%。
【解决方案2】:

它违反了封装。您不应该能够绕过父类的行为。有时能够绕过您的 自己的 类的行为(特别是在同一方法中)而不是您父母的行为是有意义的。例如,假设我们有一个基础“项目集合”,一个表示“红色项目集合”的子类和一个表示“大红色项目集合”的子类。有:

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 中的不变量被破坏了。

这有意义吗?

【讨论】:

  • 很好的例子。但是我认为当基类接受项目但派生类拒绝它们时这是糟糕的设计,因为派生不能用作基类的替代品(违反替换原则)。这是正确的想法,还是这样的层次结构干净?
  • 你的意思是组合优于继承?这个例子不是避免继承的理由——尽管还有很多其他例子。
  • @Tim:这只是一个易于理解的违反封装的示例。它可能是设置一个属性。此外,并非所有方面都会在类型级别上可见。泛型并不能解决所有问题。
  • @JohannesSchaub-litb 我认为它违反了 Liskov 替换原则,因为如果对 Item 的合同进行编码并使用 RedItems 的实例,则会得到意外的 NotRedItemException。我总是被教导一个子类应该接受一组超级输入并返回一个输出子集。换句话说,子类永远不应该拒绝对超类有效的输入或产生超类无法产生的输出,这包括抛出超类不会抛出的错误。
  • @piechuckerr:有时是书,有时是博文,有时只是体验……
【解决方案3】:

猜测,因为它不经常使用。我可以看到使用它的唯一原因是,如果您的直接父母已经覆盖了某些功能,而您正试图将其恢复为原始功能。

在我看来,这违反了 OO 原则,因为与祖父母相比,班级的直接父母应该与您的班级更密切相关。

【讨论】:

    【解决方案4】:

    除了其他人提出的非常好的观点,我认为还有一个原因:如果超类没有超类怎么办?

    由于每个类都自然地扩展(至少)Objectsuper.whatever() 将始终引用超类中的方法。但是,如果您的课程仅扩展 Object 怎么办?那么 super.super 指的是什么?应该如何处理这种行为 - 编译器错误、NullPointer 等?

    我认为不允许这样做的主要原因是它违反了封装,但这也可能是一个小原因。

    【讨论】:

    • 显然,这将是一个编译器错误 - 这是编译器确实拥有的信息。
    【解决方案5】:

    我没有足够的声誉来发表评论,所以我会将其添加到其他答案中。

    Jon Skeet 的回答非常出色,举了一个漂亮的例子。 Matt B 有一点:不是所有的超类都有超类。如果您调用没有超级的超级的超级,您的代码将会中断。

    面向对象的编程(Java 是)都是关于对象的,而不是函数。如果您想要面向任务的编程,请选择 C++ 或其他东西。如果您的对象不适合它的超类,那么您需要将它添加到“祖父类”,创建一个新类,或者找到另一个适合它的超类。

    就我个人而言,我发现这个限制是 Java 的最大优势之一。与我使用的其他语言相比,代码有些僵化,但我总是知道会发生什么。这有助于实现 Java 的“简单而熟悉”的目标。在我看来,调用 super.super 并不简单也不熟悉。也许开发人员也有同感?

    【讨论】:

    • 您说“并非所有超类都有超类”。好吧,除了 java.lang.Object 之外的所有东西都可以给你“null”。所以我想说,几乎所有人都有超级。
    • 应用程序程序员编写的每个类都有一个“超级”(java.lang.Object 没有,但应用程序程序员不会编写。)
    • 如果 super 太多,可以通过使 super.super.super...super 成为编译时错误来轻松解决。考虑到Java只有公共继承,如果有人改变了继承层次,你就是在改变接口。所以我不会担心 super^n 很可怕。
    【解决方案6】:

    我认为 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)this).x=2 ((T1)这个).x= 1 ((I)this).x=0

    (来自JLS 的示例)

    但是,这不适用于方法调用,因为方法调用是根据对象的运行时类型确定的。

    【讨论】:

    • 如果您有一个与超类中的变量同名的变量,并且由于某种原因您不能(或不允许)更改它,您仍然可以访问同名的超类变量.你问有什么用?嗯……我没用过。
    • 表达式文档的好链接。为 OCPJP 学习的人提供了一些很好的例子。
    • T1 类为什么会覆盖变量 x ?默认是静态的,对吧?
    • @amarnathharish:它没有被覆盖,默认情况下字段是包保护的非最终字段。
    • @MichaelMyers 是这样吗?那么为什么这个问题是有效的stackoverflow.com/questions/1513520/…
    【解决方案7】:

    似乎至少可以使用反射获得超类的超类的类,尽管不一定是它的实例;如果这可能有用,请考虑http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()的Javadoc

    【讨论】:

      【解决方案8】:

      我认为以下代码允许在大多数情况下使用 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);
          }
      }
      

      【讨论】:

      • 有了反射,你可以做任何事情,是的 :) 你甚至可以让字符串可变。
      • 多么可怕的事情啊!我给你 +1 只是为了弄清楚如何去做:)
      • 这是一个不错的技巧,但即使这样并不总是等同于调用不可用但需要的)super.super,这是因为 super.super 调用将携带 C 的上下文(C+B+ A)而您的答案创建了一个没有 B 和 C 上下文的 A 实例。因此,例如,如果每个 doThat 都调用 getContext() 并且 getContext 在每个类中的实现方式不同,则此答案将不起作用。在您的回答中,它将使用 A 的 getContext() 而调用不可用的 super.super 将导致使用 C 的 getContext。
      • 嗯。在某些情况下,也许您可​​以通过动态代理 (javahowto.blogspot.co.uk/2011/12/…) 克服 inor 的反对意见,将方法调用重定向到原始对象(每次调用后同步变量)?不过,代理似乎需要所有东西来实现接口。另外,我想知道 super-super 类是否可以调用它的 super-super 方法之一,特别是,你不需要重定向那些....
      • 我在另一条评论中说过,阻止 super.super. 会邀请程序员寻找新的、复杂的和令人震惊的方法来寻找解决方法,这是一个完美的例子,因为你的同事可能会非常讨厌你写这样的东西,以至于他们会亲自和从字面上射你的脚。 +1
      【解决方案9】:

      这样做有一些很好的理由。您可能有一个子类,它的方法实现不正确,但父方法实现正确。因为它属于第三方库,您可能无法/不愿意更改源。在这种情况下,您希望创建一个子类但重写一个方法来调用 super.super 方法。

      正如其他一些海报所示,可以通过反射来做到这一点,但应该可以做类似的事情

      (SuperSuperClass this).theMethod();

      我现在正在处理这个问题 - 快速解决方法是将超类方法复制并粘贴到子子类方法中:)

      【讨论】:

      • 在调用方法之前强制转换不会改变委托给的方法——它总是使用子类的实现,而不是超类的实现。参见,例如,stackoverflow.com/questions/1677993/…
      • @Larry 这正是我所处的情况,也正是我使用的修复方法。好电话!
      • 如果您有所需方法的代码,您可以打开所有需要的字段并在您的子类中运行此代码。
      【解决方案10】:

      IMO,这是在 Java 中实现 super.super.sayYourName() 行为的一种简洁方式。

      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

      【讨论】:

      • 啊,所以你提倡实现这样的类层次结构?不幸的是,如果您想从 Baby(女儿的子类)访问 Mama 中的方法,这开始变得非常混乱......
      • yakov 是对的。换句话说,这不是一个很好的例子,因为最初的问题是关于在 super.super 中调用一个被覆盖的方法。
      【解决方案11】:

      如果您认为您将需要超类,您可以在该类的变量中引用它。例如:

      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
      

      【讨论】:

      • 你的例子有点……不好,因为你使用静态方法。使用静态方法时,您根本不需要变量或超级。也许您在这里错过了一些基本的 OO 概念,请务必阅读。不得不投反对票,对不起。
      • 如果没有静态方法,它可以很容易地完成。这很简单。我打算举一个简单的例子来说明如何做到这一点。
      • 我明白你的意思,将超级变量存储在字段中是解决此问题的一种方法。但是,如果它是静态方法,则不需要该变量,您可以使用它。其次,调用静态方法一个变量是不好的做法,大多数 IDE 会警告你。如果你修正了你的答案,删除了静态的东西,我会很高兴删除我的反对票,没有冒犯。
      • 你已经接近了,但你的代码不会编译。您尝试从 main 方法中的静态上下文访问 getNumber。你真的试图编译这个吗? (你的 UltraFoo 不应该扩展 SuperFoo 吗?)
      • 我真的不想对你残忍,但是new UltraFoo.getNumber() 不会编译,因为你错过了那里的括号。但是,我刚刚删除了我的投票,因为您的代码概念现在很清楚了,谢谢!
      【解决方案12】:
      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 秒)

      【讨论】:

      • 我明白了,但是您正在创建一个新实例,所以如果对象有任何状态,这将不起作用。
      【解决方案13】:

      当您无法更改基类的代码时,调用 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 中加载真正的类。然后重写方法或构造函数。

      注意:这是绝对的hack,强烈不推荐使用,但它可以工作!由于类加载器可能存在问题,因此使用这种方法很危险。每次更新包含被覆盖类的库时,这也可能会导致问题。

      【讨论】:

        【解决方案14】:

        当架构要在代表多个派生类实现的通用 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(...);
                               }
                }
        

        但是,如果在框架和应用程序中遵循良好的架构原则,我们可以通过使用 hasA 方法而不是 isA 方法轻松避免这种情况。但在任何时候,期望设计良好的架构都不是很实际,因此需要摆脱坚实的设计原则并引入这样的技巧。 只是我的 2 美分...

        【讨论】:

          【解决方案15】:

          @Jon Skeet 很好的解释。 IMO,如果有人想调用 super.super 方法,那么必须要忽略直接父母的行为,但要访问祖父母的行为。 这可以通过instance 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 的理想做法还是好的做法,但它仍然有效。

          【讨论】:

          • 嗯,这很有创意,但并不能真正回答我的问题。 C 仍然不调用 super.super,B 只是表现不同。如果您可以更改 A 和 B,您可以添加另一个方法而不是使用 instanceof。 Super.super.foo 会在您无法访问 A 和 B 且无法更改它们的情况下为您提供帮助。
          • 同意@TimButhe,但是如果一个人想调用super.super,那么他/她有意忽略父类的行为,所以你只需要通过现有的java语法来实现这一点. (任何你想要 instanceof / 不同方法的选项)
          • 但这需要超类B知道子类C的存在。因此如果B类在第三方库中,它将无法工作
          【解决方案16】:

          如果可能的话,我会将 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,所以我发现第一个解决方案很有用。
          【解决方案17】:

          我认为这是一个破坏继承协议的问题。
          通过扩展一个类,你服从/同意它的行为、特性
          在拨打super.super.method() 时,您想打破自己的服从协议。

          你只是不能从超类中挑选出来

          但是,当您觉得需要调用 super.super.method() 时,可能会发生这种情况 - 通常是您的代码或您继承的代码中的不良设计标志!
          如果 supersuper super 类无法重构(一些遗留代码),则选择组合而不是继承。

          封装破坏是当您@Override 某些方法通过破坏封装的代码。 设计为不被覆盖的方法被标记 最终

          【讨论】:

            【解决方案18】:

            这很容易做到。例如:

            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类

            【讨论】:

              【解决方案19】:
              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();
                  }
              }
              

              输出:印在祖父中

              【讨论】:

              • 这个答案超出了问题的范围。 OP 没有询问如何调用祖父母类中的方法。问题是讨论为什么 super.super.method() 在 Java 中不是有效代码。
              【解决方案20】:

              在 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 中,您只能通过一些工具来实现这种专注。一种可能的方法是:

              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 中的所有方法都是虚拟的,因此并没有什么不同。
              【解决方案21】:

              查看this 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?
              • 是的,那是程序员的话,不是我的。在我看来,我认为有一天你可能真的需要它
              【解决方案22】:

              关键字 super 只是调用超类中方法的一种方式。 Java教程中:https://docs.oracle.com/javase/tutorial/java/IandI/super.html

              如果您的方法覆盖了其超类的方法之一,您可以通过使用关键字 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() 时,Animal 类中的方法doSth() 将打印this,它是一只猫。

              【讨论】:

                猜你喜欢
                • 2011-12-20
                • 2020-04-28
                • 2013-04-23
                • 2011-08-17
                • 2011-08-17
                • 2011-02-27
                • 1970-01-01
                • 2010-11-01
                相关资源
                最近更新 更多