我读了这个问题并认为如果有人可以写的话很容易解决(不是没有它可以解决):
@Override public String toString() { return super.super.toString(); }
我不确定它在许多情况下是否有用,但我想知道为什么它不是,如果这样的东西存在于其他语言中.
你们有什么感想?
编辑: 澄清:是的,我知道,这在Java中是不可能的,我不会真的错过它.这不是我期望的工作,并且惊讶于编译错误.我刚才有了这个想法并想讨论它.
它违反了封装.您不应该绕过父类的行为.有时候能够绕过你自己班级的行为(特别是在同一个方法中)而不是你父母的行为是有意义的.例如,假设我们有一个基础"项目集合",一个表示"红色项目集合"的子类,以及表示"大红色项目集合"的子类.有:有意义:
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
就会被破坏.
那有意义吗?
我认为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的例子)
但是,这不适用于方法调用,因为方法调用是根据对象的运行时类型确定的.
我认为以下代码允许在大多数情况下使用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 staticvoid 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); } }
我没有足够的声誉来评论,所以我会将其添加到其他答案中.
Jon Skeet以一个美丽的榜样出色地回答.Matt B有一个观点:并非所有超类都有超级.如果你打电话给一个没有超级超级的super,你的代码就会破坏.
面向对象编程(Java是)是关于对象而不是函数.如果您想要面向任务的编程,请选择C++或其他.如果你的对象不适合它的超级类,那么你需要将它添加到"祖父母类",创建一个新类,或找到它适合的另一个超类.
就个人而言,我发现这个限制是Java最大的优势之一.与我使用的其他语言相比,代码有点僵硬,但我总是知道会发生什么.这有助于Java的"简单和熟悉"目标.在我看来,调用super.super并不简单或熟悉.也许开发人员也有同样的感受?
这样做有一些很好的理由.您可能有一个子类,其子方法实现不正确,但父方法正确实现.因为它属于第三方库,您可能无法/不愿意更改源.在这种情况下,您希望创建一个子类,但重写一个方法来调用super.super方法.
正如其他一些海报所示,可以通过反射来做到这一点,但应该可以做类似的事情
(SuperSuperClass this).theMethod();
我正在处理这个问题 - 快速解决方法是将超类方法复制并粘贴到子类方法中:)
除了其他人提出的非常好的观点之外,我认为还有另一个原因:如果超类没有超类怎么办?
由于每个类自然地扩展(至少)Object
,所以super.whatever()
总是引用超类中的方法.但是,如果你的课程只延伸了Object
- 那会super.super
是什么意思呢?应该如何处理这种行为 - 编译错误,NullPointer等?
我认为不允许这样做的主要原因是它违反了封装,但这也可能是一个小原因.