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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/586363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:33:44  来源:igfitidea点击:

Why is super.super.method(); not allowed in Java?

javasuperclass

提问by Tim Büthe

I read this questionand thought that would easily be solved (not that it isn't solvable without) if one could write:

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

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

I'm not sure if it is useful in many cases, but I wonder whyit isn't and if something like this exists in other languages.

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

What do you guys think?

你们有什么感想?

EDIT:To clarify: yes I know, that's impossible in Java and I don't really miss it. This is nothing I expected to work and was surprised getting a compiler error. I just had the idea and like to discuss it.

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

采纳答案by Jon Skeet

It violates encapsulation. You shouldn't be able to bypass the parent class's behaviour. It makes sense to sometimes be able to bypass your ownclass's behaviour (particularly from within the same method) but not your parent's. For example, suppose we have a base "collection of items", a subclass representing "a collection of red items" and a subclass of that representing "a collection of big red items". It makes sense to have:

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

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

That's fine - RedItems can always be confident that the items it contains are all red. Now suppose we wereable to call super.super.add():

没关系 - 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);
    }
}

Now we could add whatever we like, and the invariant in RedItemsis broken.

现在我们可以添加任何我们喜欢的东西,并且 in 的不变量RedItems被打破了。

Does that make sense?

那有意义吗?

回答by Johannes Schaub - litb

I think if you overwrite a method and want to all the super-class version of it (like, say for equals), then you virtually always want to call the direct superclass version first, which one will call its superclass version in turn if it wants.

我认为如果你覆盖一个方法并想要它的所有超类版本(比如,说 for equals),那么你几乎总是想先调用直接超类版本,如果需要,哪个会依次调用它的超类版本.

I think it only makes rarely sense (if at all. i can't think of a case where it does) to call some arbitrary superclass' version of a method. I don't know if that is possible at all in Java. It can be done in C++:

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

this->ReallyTheBase::foo();

回答by Powerlord

At a guess, because it's not used that often. The only reason I could see using it is if your direct parent has overridden some functionality and you're trying to restore it back to the original.

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

Which seems to me to be against OO principles, since the class's direct parent should be more closely related to your class than the grandparent is.

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

回答by matt b

In addition to the very good points that others have made, I think there's another reason: what if the superclass does not have a superclass?

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

Since every class naturally extends (at least) Object, super.whatever()will always refer to a method in the superclass. But what if your class only extends Object- what would super.superrefer to then? How should that behavior be handled - a compiler error, a NullPointer, etc?

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

I think the primary reason why this is not allowed is that it violates encapsulation, but this might be a small reason too.

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

回答by EllaJo

I don't have enough reputation to comment so I will add this to the other answers.

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

Jon Skeet answers excellently, with a beautiful example. Matt B has a point: not all superclasses have supers. Your code would break if you called a super of a super that had no super.

Jon Skeet 给出了一个很好的例子。Matt B 有一个观点:并不是所有的超类都有超类。如果您调用没有 super 的 super 的 super,您的代码就会中断。

Object oriented programming (which Java is) is all about objects, not functions. If you want task oriented programming, choose C++ or something else. If your object doesn't fit in it's super class, then you need to add it to the "grandparent class", create a new class, or find another super it does fit into.

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

Personally, I have found this limitation to be one of Java's greatest strengths. Code is somewhat rigid compared to other languages I've used, but I always know what to expect. This helps with the "simple and familiar" goal of Java. In my mind, calling super.super is not simple or familiar. Perhaps the developers felt the same?

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

回答by Michael Myers

I think Jon Skeet has the correct answer. I'd just like to add that you canaccess shadowed variables from superclasses of superclasses by casting this:

我认为 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();
        }
}

which produces the output:

产生输出:

x=              3
super.x=        2
((T2)this).x=   2
((T1)this).x=   1
((I)this).x=    0

(example from the JLS)

(来自JLS 的示例)

However, this doesn't work for method calls because method calls are determined based on the runtime type of the object.

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

回答by Dexygen

It would seem to be possible to at least get the class of the superclass's superclass, though not necessarily the instance of it, using reflection; if this might be useful, please consider the Javadoc at http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()

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

回答by Nico

I think the following code allow to use super.super...super.method() in most case. (even if it's uggly to do that)

我认为以下代码允许在大多数情况下使用 super.super...super.method() 。(即使这样做很丑)

In short

简而言之

  1. create temporary instance of ancestor type
  2. copy values of fields from originalobject to temporary one
  3. invoke target method on temporary object
  4. copy modified values back to original object
  1. 创建祖先类型的临时实例
  2. 将字段值从原始对象复制到临时对象
  3. 在临时对象上调用目标方法
  4. 将修改后的值复制回原始对象

Usage :

用法 :

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

回答by Larry Watanabe

There's some good reasons to do this. You might have a subclass which has a method which is implemented incorrectly, but the parent method is implemented correctly. Because it belongs to a third party library, you might be unable/unwilling to change the source. In this case, you want to create a subclass but override one method to call the super.super method.

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

As shown by some other posters, it is possible to do this through reflection, but it should be possible to do something like

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

(SuperSuperClass this).theMethod();

(SuperSuperClass this).theMethod();

I'm dealing with this problem right now - the quick fix is to copy and paste the superclass method into the subsubclass method :)

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

回答by Yakov Fain

IMO, it's a clean way to achieve super.super.sayYourName()behavior in Java.

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

Output:

输出:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha