Why is super.super.method(); not allowed in Java?
我读了这个问题,认为如果一个人能写:
1 2 3 4 |
我不确定它在很多情况下是否有用,但我想知道为什么不有用,以及在其他语言中是否存在类似的东西。
你们觉得怎么样?
编辑:澄清:是的,我知道,这在Java中是不可能的,我并不真的错过它。这不是我所期望的工作,我惊讶地发现了一个编译器错误。我刚刚有了这个想法,我想讨论一下。
它违反了封装。您不应该能够绕过父类的行为。有时可以绕过自己类的行为(尤其是同一方法中的行为)是有意义的,但不能绕过父类的行为。例如,假设我们有一个基"项集合",一个表示"红色项集合"的子类,以及一个表示"红色大项集合"的子类。有以下条件是有意义的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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():
1 2 3 4 5 6 7 8 9 | 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); } } |
现在我们可以随意添加,
这有道理吗?
我认为乔恩·斯基特的回答是正确的。我想补充一点,您可以通过强制执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); } } |
产生输出:
1 2 3 4 5 | x= 3 super.x= 2 ((T2)this).x= 2 ((T1)this).x= 1 ((I)this).x= 0 |
(来自JLS的示例)
但是,这对方法调用不起作用,因为方法调用是根据对象的运行时类型确定的。
我认为下面的代码允许在大多数情况下使用super.super…super.method()。(即使这样做很糟糕)
简而言之
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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); } } |
我没有足够的声誉来发表评论,所以我会将此添加到其他答案中。
乔恩·斯基特以一个很好的例子回答了问题。马特B有一点:不是所有的超类都有超类。如果你称一个超级的超级没有超级,你的代码就会被破坏。
面向对象的编程(Java)是关于对象,而不是函数。如果您想要面向任务的编程,选择C++或其他东西。如果你的对象不适合它的超级类,那么你需要将它添加到"祖父母类",创建一个新类,或者找到另一个它适合的超级类。
就我个人而言,我发现这种局限性是Java最大的优势之一。与我使用的其他语言相比,代码有点死板,但我总是知道该期待什么。这有助于Java的"简单而熟悉"的目标。在我看来,叫super.super并不简单,也不熟悉。也许开发人员也有同样的感受?
有一些很好的理由这样做。您可能有一个子类,该子类有一个方法实现不正确,但父方法实现正确。因为它属于第三方库,所以您可能无法/不愿意更改源。在这种情况下,您希望创建一个子类,但要重写一个方法来调用super.super方法。
如其他一些海报所示,通过反射可以做到这一点,但应该可以做到
(supersuperclass this).themethod();
我现在正在处理这个问题-快速解决方法是将超类方法复制并粘贴到子类方法中:)
除了其他人提出的非常好的观点外,我认为还有另一个原因:如果超类没有超类呢?
由于每个类都自然地扩展(至少)
我认为不允许这样做的主要原因是它违反了封装,但这可能也是一个很小的原因。
我认为如果你覆盖了一个方法,并且想要它的所有超类版本(比如说
我认为这只是很少有意义(如果有的话)。我想不出在哪种情况下调用某个任意超类的方法版本。我不知道在Java中是否有可能。它可以在C++中完成:
1 | this->ReallyTheBase::foo(); |
看看这个github项目,尤其是objecthandle变量。这个项目展示了如何实际而准确地调用孙子的祖父母方法。
为了防止链接被破坏,下面是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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"; } } } |
快乐编码!!!!!
大概吧,因为它不经常使用。我能看到使用它的唯一原因是,如果您的直接家长已经覆盖了某些功能,并且您正试图将其恢复到原始状态。
在我看来,这违背了OO原则,因为这个班的直系父母应该比祖父母更接近你的班。
如果可能的话,我会把super.super方法体放在另一个方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class SuperSuperClass { public String toString() { return DescribeMe(); } protected String DescribeMe() { return"I am super super"; } } class SuperClass extends SuperSuperClass { public String toString() { return"I am super"; } } class ChildClass extends SuperClass { public String toString() { return DescribeMe(); } } |
或者,如果您无法更改超级类,可以尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class SuperSuperClass { public String toString() { return"I am super super"; } } class SuperClass extends SuperSuperClass { public String toString() { return DescribeMe(super.toString()); } protected String DescribeMe(string fromSuper) { return"I am super"; } } class ChildClass extends SuperClass { protected String DescribeMe(string fromSuper) { return fromSuper; } } |
在这两种情况下,
1 | new ChildClass().toString(); |
"我是超级超级"的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public class A { @Override public String toString() { return"A"; } } public class B extends A { @Override public String toString() { return"B"; } } public class C extends B { @Override public String toString() { return"C"; } } public class D extends C { @Override public String toString() { String result =""; try { result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString(); } catch (InstantiationException ex) { Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex); } return result; } } public class Main { public static void main(String... args) { D d = new D(); System.out.println(d); } } |
运行:一生成成功(总时间:0秒)
@乔恩·斯基特解释得很好。IMO如果有人想调用super.super方法,那么必须要忽略直接父对象的行为,但要访问父对象的行为。这可以通过实例来实现。以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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(); } } |
这是司机班,
1 2 3 4 5 6 |
其输出将是
1 2 | In C Class In A Class |
在这种情况下,B类printclass行为将被忽略。我不确定这是否是实现super.super的理想或良好实践,但它仍然有效。
我曾经遇到过这样的情况,当体系结构要在一个公共的CustomBaseClass中构建公共功能时,该类代表几个派生类实现。但是,我们需要绕过特定派生类的特定方法的公共逻辑。在这种情况下,我们必须使用super.super.methodx实现。
我们通过在CustomBaseClass中引入一个布尔成员来实现这一点,该成员可用于有选择地推迟自定义实现,并在需要时向默认框架实现让步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ... FrameworkBaseClass (....) extends... { methodA(...){...} methodB(...){...} ... methodX(...) ... methodN(...){...} } /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/ CustomBaseClass(...) extends FrameworkBaseClass { private boolean skipMethodX=false; /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/ methodA(...){...} methodB(...){...} ... methodN(...){...} methodX(...){ if (isSkipMethodX()) { setSKipMethodX(false); super.methodX(...); return; } ... //common method logic } } DerivedClass1(...) extends CustomBaseClass DerivedClass2(...) extends CustomBaseClass ... DerivedClassN(...) extends CustomBaseClass... DerivedClassX(...) extends CustomBaseClass... { methodX(...){ super.setSKipMethodX(true); super.methodX(...); } } |
然而,在框架和应用程序中遵循良好的体系结构原则,我们可以通过使用HASA方法而不是ISA方法轻松避免这种情况。但在任何时候,期望设计良好的体系结构到位都不是很实际,因此需要摆脱可靠的设计原则,引入这样的黑客。只是我的2美分…
当不能更改基类的代码时,调用super.super.method()是有意义的。这通常在扩展现有库时发生。
先问问你自己,你为什么要延长这门课?如果答案是"因为我不能更改它",那么您可以在应用程序中创建精确的包和类,并重写调皮的方法或创建委托:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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() {...} ... } |
例如,您可以在应用程序中创建org.springframework.test.context.junit4.springjunit4classrunner类,这样这个类应该在jar中的实际类之前加载。然后重写方法或构造函数。
注意:这是绝对黑客,强烈建议不要使用,但它是工作的!使用这种方法是危险的,因为类加载器可能有问题。此外,每次更新包含被覆盖类的库时,这可能会导致问题。
通过反射可以获得超类的超类,但未必是它的实例;如果这是有用的,请考虑http://java.Sun.com /J2SE/1.5.0/DOCS/API/Java/Lang/Copy.html GyGu超类()的JavaDoc。
如果您认为需要超类,可以在该类的变量中引用它。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class Foo { public int getNumber() { return 0; } } public class SuperFoo extends Foo { public static Foo superClass = new Foo(); public int getNumber() { return 1; } } public class UltraFoo extends Foo { public static void main(String[] args) { System.out.println(new UltraFoo.getNumber()); System.out.println(new SuperFoo().getNumber()); System.out.println(new SuperFoo().superClass.getNumber()); } public int getNumber() { return 2; } } |
应该打印出来:
1 2 3 | 2 1 0 |
我认为这是一个破坏继承协议的问题。通过扩展一个类,你服从/同意它的行为、特性当你打电话给
你就是不能从超级班里挑选。
但是,当您觉得需要调用
在C中,您可以调用类似以下任何祖先的方法:
1 2 3 4 5 6 7 8 9 10 | public class A internal virtual void foo() ... public class B : A public new void foo() ... public class C : B public new void foo() { (this as A).foo(); } |
你也可以在Delphi中这样做:
1 2 3 4 5 6 7 8 9 10 11 | type A=class procedure foo; ... B=class(A) procedure foo; override; ... C=class(B) procedure foo; override; ... A(objC).foo(); |
但在爪哇,你只能通过一些齿轮来实现这种专注。一种可能的方法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | class A { int y=10; void foo(Class X) throws Exception { if(X!=A.class) throw new Exception("Incorrect parameter of"+this.getClass().getName()+".foo("+X.getName()+")"); y++; System.out.printf("A.foo(%s): y=%d ",X.getName(),y); } void foo() throws Exception { System.out.printf("A.foo() "); this.foo(this.getClass()); } } class B extends A { int y=20; @Override void foo(Class X) throws Exception { if(X==B.class) { y++; System.out.printf("B.foo(%s): y=%d ",X.getName(),y); } else { System.out.printf("B.foo(%s) calls B.super.foo(%s) ",X.getName(),X.getName()); super.foo(X); } } } class C extends B { int y=30; @Override void foo(Class X) throws Exception { if(X==C.class) { y++; System.out.printf("C.foo(%s): y=%d ",X.getName(),y); } else { System.out.printf("C.foo(%s) calls C.super.foo(%s) ",X.getName(),X.getName()); super.foo(X); } } void DoIt() { try { System.out.printf("DoIt: foo(): "); foo(); Show(); System.out.printf("DoIt: foo(B): "); foo(B.class); Show(); System.out.printf("DoIt: foo(A): "); foo(A.class); Show(); } catch(Exception e) { //... } } void Show() { System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d ", ((A)this).y, ((B)this).y, ((C)this).y); } } |
objc.doit()结果输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | DoIt: foo(): A.foo() C.foo(C): y=31 Show: A.y=10, B.y=20, C.y=31 DoIt: foo(B): C.foo(B) calls C.super.foo(B) B.foo(B): y=21 Show: A.y=10, B.y=21, C.y=31 DoIt: foo(A): C.foo(A) calls C.super.foo(A) B.foo(A) calls B.super.foo(A) A.foo(A): y=11 Show: A.y=11, B.y=21, C.y=31 |
IMO,这是在Java中实现EDCOX1·0行为的一种干净方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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); } } |
输出:
关键字super只是在超类中调用方法的一种方法。在Java教程中:HTTPS://DOCS.Oracle .COM/JavaSe/TutoRale/Java/IANDI/Suff.HTML
如果方法重写了其超类的某个方法,则可以通过使用关键字super来调用被重写的方法。不要相信它是超级物体的参考!!!!不,它只是一个在超类中调用方法的关键字。
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
当调用
这很简单。例如:
B的C子类和A的B子类。例如,这三个子类都有methodname()方法。
公共抽象类A{
1 2 3 |
}
公共B类扩展A{
1 2 3 4 5 6 7 8 9 | public void methodName() { super.methodName(); System.out.println("Class B"); } // Will call the super methodName public void hackSuper() { super.methodName(); } |
}
公共C类扩展B{
1 2 3 4 5 6 7 8 9 10 11 |
}
运行C类输出为:甲类C类
而不是输出:甲类乙类C类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class SubSubClass extends SubClass { @Override public void print() { super.superPrint(); } public static void main(String[] args) { new SubSubClass().print(); } } class SuperClass { public void print() { System.out.println("Printed in the GrandDad"); } } class SubClass extends SuperClass { public void superPrint() { super.print(); } } |
输出:在爷爷家打印