@Override
public String toString() {
return super.super.toString();
}
我不确定它在许多情况下是否有用,但我想知道为什么它不是这样,并且其他语言中是否存在类似的东西。
你们觉得呢?
编辑: 澄清一下:是的,我知道在Java中这是不可能的,我也不是很想要它。这不是我期望它能工作,而是惊讶于得到编译器错误。我只是有这个想法,想讨论一下。
@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 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);
}
}
super.super.
会引导程序员寻找新的、复杂的和极端错误的方法来绕过它,这是一个完美的例子,因为你的同事们可能会因为你写了这样的代码而非常讨厌你,以至于他们会亲自并真正地朝你的脚开枪。+1 - Braden Best我声望不够高,无法发表评论,所以我会将这个回答添加到其他回答中。
Jon Skeet 给出了一个非常好的例子作为答案。Matt B也有一定道理:并非所有的超类都拥有超类。如果你调用了没有超类的超类的超级方法,你的代码就会出错。
面向对象编程(Java就是如此)是关于对象而不是函数的。如果你想要任务导向的编程,请选择C ++或其他语言。如果你的对象不适合它的超类,那么你需要将其添加到“爷爷类”,创建一个新类,或找到另一个适合它的超类。
个人认为,这种限制是Java最大的优点之一。与我使用过的其他语言相比,代码有些死板,但我总是知道该期望什么。这有助于实现Java的“简单和熟悉”的目标。在我看来,调用super.super并不简单或熟悉。也许开发人员也有同感?
this->ReallyTheBase::foo();
当你无法更改基类代码时,调用super.super.method()是有意义的。这通常发生在您扩展现有库时。
首先问问自己,为什么要扩展该类?如果答案是“因为我无法更改它”,那么您可以在应用程序中创建完全相同的包和类,并重写naughty方法或创建委托:
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() {...}
...
}
请看这个 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";
}
}
}
愉快的编码!!!
@Jon Skeet 很好的解释。 我认为,如果有人想调用super.super方法,则必须要忽略直接父元素的行为,但是想要访问祖先元素的行为。 可以通过 instance 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
这种情况下,Class B printClass的行为将被忽略。 我不确定这是否是实现super.super的理想或良好实践,但它仍然可以工作。
super.super.toString()
与你选择扩展一个类并接受它的所有特性的决定相矛盾,因此不建议这样做。 - DayaMoon