Answers:
它违反了封装。您不应该能够绕过父类的行为。有时能够绕过您自己的类的行为(尤其是在同一方法中)而不是您父母的行为是有意义的。例如,假设我们有一个基本的“项目集合”,一个代表“红色项目集合”的子类,一个代表“大红色项目集合”的子类。有意义的是:
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);
}
}
现在我们可以添加所需的任何内容,并且in不变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 ((Ithis))。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
我的信誉不足,无法发表评论,因此将其添加到其他答案中。
乔恩·斯基特(Jon Skeet)给出了一个很好的例子,给出了很好的回答。Matt B有一点:并非所有超类都具有超类。如果您调用没有父级的父级的父级,则代码将中断。
面向对象的编程(Java是)全部与对象有关,而不是函数。如果要面向任务的编程,请选择C ++或其他方式。如果您的对象不适合其父类,则需要将其添加到“祖父母类”中,创建一个新类,或找到它适合的另一个父类。
就个人而言,我发现此限制是Java的最大优势之一。与我使用的其他语言相比,代码有些僵化,但是我总是知道会发生什么。这有助于实现Java的“简单和熟悉”的目标。在我看来,调用super.super并不简单或熟悉。也许开发人员有同样的感觉?
这样做有一些充分的理由。您可能有一个子类,该子类的方法实现不正确,但是父方法实现正确。因为它属于第三方库,所以您可能无法/不愿意更改源。在这种情况下,您想创建一个子类,但重写一个方法来调用super.super方法。
如其他海报所示,可以通过反射来实现,但是应该可以做到
(this的SuperSuperClass).theMethod();
我现在正在处理此问题-快速的解决方法是将超类方法复制并粘贴到subsubclass方法中:)
除了别人提出的很好的观点外,我认为还有另一个原因:如果超类没有超类怎么办?
由于每个类都会自然地扩展(至少)Object
,因此super.whatever()
将始终引用超类中的方法。但是,如果您的班级仅扩展Object
了super.super
,那该怎么办呢?应该如何处理该行为-编译器错误,NullPointer等?
我认为不允许这样做的主要原因是它违反了封装,但这也可能是一个很小的原因。
我认为,如果您覆盖一个方法并想要该方法的所有超类版本(例如代表equals
),那么您实际上总是想先调用直接超类版本,如果需要,它将依次调用其超类版本。
我认为调用某个方法的任意超类版本几乎是没有意义的(如果有的话。我想不起来)。我不知道这在Java中是不可能的。可以用C ++完成:
this->ReallyTheBase::foo();
查看这个 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";
}
}
}
编码愉快!!!
如果可能的话,我会把super.super方法的主体放在另一个方法中
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();
}
}
或者,如果您不能更改super-super类,则可以尝试以下操作:
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;
}
}
在这两种情况下,
new ChildClass().toString();
结果为“我是超级超级”
似乎至少可以使用反射来获得超类的超类的类,尽管不一定是它的实例。如果这可能有用,请考虑Javadoc,网址为http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()
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秒)
当您无法更改基类的代码时,调用super.super.method()才有意义。当扩展现有库时,通常会发生这种情况。
首先问问自己,你为什么要扩大课堂?如果答案是“因为我无法更改”,那么您可以在应用程序中创建确切的包和类,并重写顽皮的方法或创建委托:
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真正的类之前加载该类。然后重写方法或构造函数。
注意:这是绝对的技巧,强烈建议您不要使用它,但是这很正常!由于类加载器可能存在问题,因此使用此方法很危险。同样,这可能会在您每次更新包含覆盖类的库时引起问题。
当架构要在代表几个派生类实现的通用CustomBaseClass中构建通用功能时,我曾遇到过类似情况。但是,我们需要针对特定派生类的特定方法规避通用逻辑。在这种情况下,我们必须使用super.super.methodX实现。
我们通过在CustomBaseClass中引入布尔成员来实现此目的,该布尔成员可用于有选择地推迟自定义实现并在需要时屈服于默认框架实现。
...
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美分...
@Jon Skeet很好的解释。IMO如果有人要调用super.super方法,则必须要忽略直接父级的行为,但要访问大父级的行为。这可以通过实例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
在这种情况下,将忽略B类printClass行为。我不确定这是否是实现super.super的理想或良好做法,但仍然有效。
如果您认为将需要超类,则可以在该类的变量中引用它。例如:
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;
}
}
应该打印出来:
2
1
0
new UltraFoo.getNumber()
不会编译,因为你在那儿错过了括号。但是,由于您的代码概念现在已经很清楚了,我刚刚删除了我的注释,谢谢!
IMO,这是一种实现super.super.sayYourName()
Java行为的干净方法。
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);
}
}
输出:
Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha
在C#中,您可以像这样调用任何祖先的方法:
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中执行此操作:
type
A=class
procedure foo;
...
B=class(A)
procedure foo; override;
...
C=class(B)
procedure foo; override;
...
A(objC).foo();
但是在Java中,您只能通过一些操作来实现这种关注。一种可能的方法是:
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\n",X.getName(),y);
}
void foo() throws Exception {
System.out.printf("A.foo()\n");
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\n",X.getName(),y);
} else {
System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",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\n",X.getName(),y);
} else {
System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
super.foo(X);
}
}
void DoIt() {
try {
System.out.printf("DoIt: foo():\n");
foo();
Show();
System.out.printf("DoIt: foo(B):\n");
foo(B.class);
Show();
System.out.printf("DoIt: foo(A):\n");
foo(A.class);
Show();
} catch(Exception e) {
//...
}
}
void Show() {
System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
}
}
objC.DoIt()结果输出:
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
这很容易做到。例如:
B的C子类和A的B子类。这三个子类都具有方法methodName()。
public abstract class A {
public void methodName() {
System.out.println("Class A");
}
}
public class B extends A {
public void methodName() {
super.methodName();
System.out.println("Class B");
}
// Will call the super methodName
public void hackSuper() {
super.methodName();
}
}
public class C extends B {
public static void main(String[] args) {
A a = new C();
a.methodName();
}
@Override
public void methodName() {
/*super.methodName();*/
hackSuper();
System.out.println("Class C");
}
}
运行C类,输出将是:A类C类
代替输出:A类B类C类
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();
}
}
输出:在GrandDad中打印
super.super.method()
Java 中为什么无效代码的讨论。
关键字super只是调用超类中的方法的一种方法。在Java教程中:https : //docs.oracle.com/javase/tutorial/java/IandI/super.html
不要相信这是超级对象的参考!!!不,这只是调用超类中的方法的关键字。
这是一个例子:
class Animal {
public void doSth() {
System.out.println(this); // It's a Cat! Not an animal!
System.out.println("Animal do sth.");
}
}
class Cat extends Animal {
public void doSth() {
System.out.println(this);
System.out.println("Cat do sth.");
super.doSth();
}
}
当您调用时cat.doSth()
,将打印doSth()
类中的方法,它就是一只猫。Animal
this
super.super.toString()
当您选择扩展类从而接受其所有(而非部分)功能时,想要调用会与您自己的决定相矛盾。