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

问题

我读了this question并且认为如果可以写的话很容易解决(不是没有它可以解决):

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

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

你们有什么感想?

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


#1 热门回答(434 赞)

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

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

那有意义吗?


#2 热门回答(61 赞)

我认为Jon Skeet有正确的答案。我想通过castthis添加你可以从超类的超类中获取阴影变量:

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 热门回答(37 赞)

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