当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么是super.super.method();Java不允许?》经验,为你挑选了6个好方法。

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

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

我不确定它在许多情况下是否有用,但我想知道为什么它不是,如果这样的东西存在于其他语言中.

你们有什么感想?

编辑: 澄清:是的,我知道,这在Java中是不可能的,我不会真的错过它.这不是我期望的工作,并且惊讶于编译错误.我刚才有了这个想法并想讨论它.



1> Jon Skeet..:

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

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就会被破坏.

那有意义吗?


很好的例子.但我认为当基类接受项目时设计很糟糕,但派生类拒绝它们,因为派生类不能用作基类的替代品(违反替换原则).是正确的思考,还是这样的层次结构清洁?
@ JohannesSchaub-litb我认为它违反了Liskov替换原则,因为如果一个代码编写Item的契约并使用RedItems的实例,那么将会出现意外的NotRedItemException.我总是被告知子类应该采用超级输入集并返回输出的子集.换句话说,子类永远不应该拒绝对超类有效的输入或者产生超类不能产生的输出,这包括抛出超类不抛出的错误.
你的意思是构成而不是继承吗?这个例子不是避免继承的理由 - 尽管还有很多其他的.
@Tim:这只是一个容易理解的违反封装的例子.它可能是设置属性而不是.此外,并非所有方面都在类型级别可见.泛型不是一切的答案.
关于这个评论:"现在我们可以添加任何我们喜欢的东西,并且RedItems中的不变量被破坏了."如果你在添加覆盖代码之前没有调用`super.add(item);`,那么这将是真的.但是我理解你的整体观点并且弄清楚为什么从`add(item)`调用`super.super.add(item)`是错误的.
@piechuckerr:有时书籍,有时是博客文章,有时只是体验......
是的,我认为我也同意琼.超类可以很好地说,子类中的行为可以更严格 - 然后我认为层次结构很好.
我知道这样做不是一个好的设计,但我认为这不足以阻止这种语言.我因此而遇到很大麻烦,因为我需要修复RedItems.add中的一个错误并且没有它的来源.但是如果不在NaughtyItems.add中使用Items.add,就无法修复bug.
想知道为什么C++允许这个...我猜不同的范例?http://stackoverflow.com/questions/357307/c-how-to-call-a-parent-class-function-from-derived-class-function

2> Michael Myer..:

我认为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
((I)this).x=    0

(来自JLS的例子)

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


如果变量的名称与超类中的变量名称相同,并且由于某种原因您不能(或不允许)更改它,您仍然可以使用相同的名称访问超类的变量.你问,有什么用?嗯......我从来没用过它.

3> 小智..:

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

简而言之

    创建祖先类型的临时实例

    原始对象的字段值复制到临时对象

    在临时对象上调用目标方法

    将修改后的值复制回原始对象

用法:

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  void exec(Class 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  void shareVars(Class 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.

4> 小智..:

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

Jon Skeet以一个美丽的榜样出色地回答.Matt B有一个观点:并非所有超类都有超级.如果你打电话给一个没有超级超级的super,你的代码就会破坏.

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

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


你说"并非所有的超级班都都超级".好吧,除了java.lang.Object之外什么都可以给你"null".所以我想说,几乎所有人都有超级.
如果super.super.super ... super太多,可以通过使super.super.super ... super成为编译时错误来轻松解决。考虑到Java仅具有公共继承,因此如果有人更改了继承层次结构,则需要更改接口。因此,我不会担心super ^ n会令人恐惧。

5> Larry Watana..:

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

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

(SuperSuperClass this).theMethod();

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



6> matt b..:

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

由于每个类自然地扩展(至少)Object,所以super.whatever()总是引用超类中的方法.但是,如果你的课程只延伸了Object- 那会super.super是什么意思呢?应该如何处理这种行为 - 编译错误,NullPointer等?

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


显然,这将是一个编译器错误 - 它是编译器确实具有的信息.
推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有