为什么是super.super.method(); Java中不允许使用?


360

我读了这个问题,并认为如果可以写的话,很容易解决(不是没有它就不能解决):

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

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

你们有什么感想?

编辑: 澄清一下:是的,我知道,这在Java中是不可能的,我也不是很想念它。这不是我期望的结果,并且惊讶于出现编译器错误。我只是有这个想法,喜欢讨论它。


7
super.super.toString()当您选择扩展类从而接受其所有(而非部分)功能时,想要调用会与您自己的决定相矛盾。
DayaMoon 2014年

Answers:


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

现在我们可以添加所需的任何内容,并且in不变RedItems

那有意义吗?


38
很好的例子。但是我认为当基类接受项目但派生类拒绝项目时这是不好的设计,因为派生不能用作基类的直接替代(违反替代原则)。是正确的想法,还是这样的等级制度是干净的?
Johannes Schaub-litb

5
你是说组成比继承吗?这个例子并不是避免继承的理由-尽管还有很多其他的例子。
乔恩·斯基特

3
@Tim:这只是违反封装的一个容易理解的例子。它可能正在设置一个属性。此外,并非所有方面在类型级别上都是可见的。泛型并不能解决所有问题。
乔恩·斯基特

12
@ JohannesSchaub-litb我认为它违反了Liskov替换原则,因为如果一个代码编码到Item的合同并使用RedItems的一个实例,则将得到意外的NotRedItemException。我总是被教导子类应该接受一组超级输入,并返回一个输出子集。换句话说,子类绝不应该拒绝对超类有效的输入或产生超类无法产生的输出,这包括抛出超类不会抛出的错误。
Konstantin Tarashchanskiy 2012年

4
@piechuckerr:有时是书籍,有时是博客文章,有时只是体验……
乔恩·斯凯特

72

我认为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
super.x = 2
((T2)this).x = 2
((T1)this).x = 1
((Ithis))。x = 0

(来自JLS的示例)

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


6
如果您的变量与超类中的变量具有相同的名称,并且由于某种原因您无法(或不允许)对其进行更改,则仍可以使用相同的名称访问超类的变量。你问什么用?好吧...我没用过。
迈克尔·迈尔斯

链接到表达式文档的链接。对于为OCPJP学习的人们提供了一些很好的例子。
Gordon

太棒了 我从来没有想过要使用演员表。
Thomas Eding

T1类如何覆盖变量x?默认情况下是静态的final吗?
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)的上下文,而您的答案将创建一个实例因此,如果每个调用getContext()的doThat和每个类中的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的最大优势之一。与我使用的其他语言相比,代码有些僵化,但是我总是知道会发生什么。这有助于实现Java的“简单和熟悉”的目标。在我看来,调用super.super并不简单或熟悉。也许开发人员有同样的感觉?


3
您说“并非所有超类都具有超类”。好吧,除了java.lang.Object之外的所有东西都可以给您“空”。所以我想说,几乎所有的都有超级。
TimBüthe09年

应用程序程序员编写的每个类都有一个“超级”(java.lang.Object没有,但应用程序程序员没有编写。)
finnw

2
如果super.super.super ... super太多,可以通过使super.super.super ... super成为编译时错误来轻松解决。考虑到Java仅具有公共继承,因此如果有人更改了继承层次结构,则需要更改接口。因此,我不会担心super ^ n会令人恐惧。
Thomas Eding

7

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

如其他海报所示,可以通过反射来实现,但是应该可以做到

(this的SuperSuperClass).theMethod();

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


1
在调用方法之前进行转换不会更改委托给的方法-始终使用子类的实现,而不是上级的实现。参见例如stackoverflow.com/questions/1677993/…–
约书亚·戈德堡

1
@Larry这正是我所处的情况,也正是我所使用的解决方法。好决定!
bcr 2012年

如果您具有所需方法的代码,则可以打开所有必需的字段并在子类中运行此代码。
Enyby 2015年

6

除了别人提出的很好的观点外,我认为还有另一个原因:如果超类没有超类怎么办?

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

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


4
显然,这将是编译器错误-这是编译器确实具有的信息。
Michael Borgwardt

4

我认为,如果您覆盖一个方法并想要该方法的所有超类版本(例如代表equals),那么您实际上总是想先调用直接超类版本,如果需要,它将依次调用其超类版本。

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

this->ReallyTheBase::foo();

6
如果有人编写的子类的一种方法实现不正确,但超类方法完成了90%的工作,那是有道理的。然后,您想创建一个子类,并覆盖调用超类超类方法的方法,并添加自己的10%。
拉里·渡边

3

一个猜测,因为它不经常使用。我可以看到使用它的唯一原因是,如果您的直接父级已重写某些功能,而您正尝试将其还原回原始状态。

在我看来,这与OO原则背道而驰,因为与父辈相比,班级的直接父级应该与您的班级关系更紧密。


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

编码愉快!!!


您的科学家非常关注是否可以,他们不停地思考是否应该这样做。警:请不要真正做到这一点... 🤔
蒂姆·布泰

是的,那是编码员的话,不是我的。以我的观点,我认为您可能某天实际上需要它
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();
    }
}

或者,如果您不能更改super-super类,则可以尝试以下操作:

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


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


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

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


1

@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的理想或良好做法,但仍然有效。


1
嗯,这很有创意,但并不能真正回答我的问题。C仍然没有调用super.super,B的行为有所不同。如果可以更改A和B,则可以添加另一种方法,而不是使用instanceof。如果您无权访问A和B并且无法更改它们,Super.super.foo将为您提供帮助。
TimBüthe2013年

同意@TimButhe,但是如果要调用super.super,则他/她有意忽略父类的行为,因此您只需要通过现有的Java语法来实现。(任何选项,您都希望使用instanceof /不同的方法)
Sanjay Jain 2013年

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
您的例子有点……很糟糕,因为您使用静态方法。使用静态方法时,根本不需要变量或super。也许您在这里错过了一些基本的OO概念,请确保您已读懂。必须投票,对不起。
TimBüthe2011年

1
如果没有静态方法,很容易做到这一点。很简单。我正在寻找一个有关如何执行此操作的简单示例。
Ashtheking 2011年

我明白了,将超变量存储在字段中是解决此问题的一种方法。但是,如果该变量是静态方法,则不需要它,您可以使用它。其次,将静态方法与变量一起调用是一种不好的做法,大多数IDE都会警告您。如果您解决了问题,删除了静态内容,我将很高兴删除我的不赞成票,这没有冒犯。
TimBüthe2011年

您已接近,但您的代码将无法编译。您尝试通过main方法中的静态上下文访问getNumber。您是否真的尝试过编译?(您的UltraFoo是否不应该扩展SuperFoo?)
TimBüthe2011年

我真的不想对你残忍,但new UltraFoo.getNumber()不会编译,因为你在那儿错过了括号。但是,由于您的代码概念现在已经很清楚了,我刚刚删除了我的注释,谢谢!
TimBüthe2011年

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


啊,所以您提倡实现这样的类层次结构?不幸的是,如果您想从Baby(Daughter的子类)中访问Mama中的方法,这将变得非常混乱……
bcr 2012年

亚科夫·法因是对的。换句话说,这不是一个好例子,因为最初的问题是关于在super.super中调用重写的方法。
inor 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中,您只能通过一些操作来实现这种关注。一种可能的方法是:

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

0

这很容易做到。例如:

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类


-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 中为什么无效代码的讨论。
杰德·沙夫

-1

关键字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(),将打印doSth()类中的方法,它就是一只猫。Animalthis

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.