关于超类:为什么super.super.method(); 在Java中不允许?

Why is super.super.method(); not allowed in Java?

我读了这个问题,认为如果一个人能写:

1
2
3
4
@Override
public String toString() {
    return super.super.toString();
}

我不确定它在很多情况下是否有用,但我想知道为什么不有用,以及在其他语言中是否存在类似的东西。

你们觉得怎么样?

编辑:澄清:是的,我知道,这在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);
    }
}

现在我们可以随意添加,RedItems中的不变量被破坏了。

这有道理吗?


我认为乔恩·斯基特的回答是正确的。我想补充一点,您可以通过强制执行this从超类的超类访问阴影变量:

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();

    我现在正在处理这个问题-快速解决方法是将超类方法复制并粘贴到子类方法中:)


    除了其他人提出的非常好的观点外,我认为还有另一个原因:如果超类没有超类呢?

    由于每个类都自然地扩展(至少)Object,因此super.whatever()总是引用超类中的方法。但是如果你的类只扩展了Object--那么super.super指的是什么?该如何处理这种行为——编译器错误、空指针等?

    我认为不允许这样做的主要原因是它违反了封装,但这可能也是一个很小的原因。


    我认为如果你覆盖了一个方法,并且想要它的所有超类版本(比如说equals的超类版本),那么你实际上总是想先调用直接超类版本,如果它想要的话,它会依次调用它的超类版本。

    我认为这只是很少有意义(如果有的话)。我想不出在哪种情况下调用某个任意超类的方法版本。我不知道在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
    public class Driver {
        public static void main(String[] args) {
            C c = new C();
            c.printClass();
        }
    }

    其输出将是

    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


    我认为这是一个破坏继承协议的问题。通过扩展一个类,你服从/同意它的行为、特性当你打电话给super.super.method()时,你想破坏你自己的服从协议。

    你就是不能从超级班里挑选。

    但是,当您觉得需要调用super.super.method()时,可能会发生这种情况——通常在您的代码或继承的代码中是一个糟糕的设计符号!如果超级类和超级类不能重构(一些遗留代码),那么选择组合而不是继承。封装中断是指您@override some methods by breaking the capsuled code.将标记设计为不重写的方法决赛。


    在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);
        }
    }

    输出:

    Request to lie: d.sayYourName(true) returns Grandma FedoraRequest not to lie: d.sayYourName(false) returns Little girl Masha


    关键字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
    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()时,类Animal中的方法doSth()将打印this,它是一个cat。


    这很简单。例如:

    B的C子类和A的B子类。例如,这三个子类都有methodname()方法。

    公共抽象类A{

    1
    2
    3
    public void methodName() {
        System.out.println("Class A");
    }

    }

    公共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
    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类输出为:甲类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();
        }
    }

    输出:在爷爷家打印