385

I read this question and thought that would easily be solved (not that it isn't solvable without) if one could write:

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

I'm not sure if it is useful in many cases, but I wonder why it isn't and if something like this exists in other languages.

What do you guys think?

EDIT: To clarify: yes I know, that's impossible in Java and I don't really miss it. This is nothing I expected to work and was surprised getting a compiler error. I just had the idea and like to discuss it.

Community
  • 1
  • 1
Tim Büthe
  • 58,799
  • 16
  • 82
  • 126
  • 7
    Wanting to call `super.super.toString()` contradicts your own decision when you choose to extend a class thus accepting **all** (not some of) its features. – DayaMoon Apr 09 '14 at 08:20

22 Answers22

514

It violates encapsulation. You shouldn't be able to bypass the parent class's behaviour. It makes sense to sometimes be able to bypass your own class's behaviour (particularly from within the same method) but not your parent's. For example, suppose we have a base "collection of items", a subclass representing "a collection of red items" and a subclass of that representing "a collection of big red items". It makes sense to have:

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

That's fine - RedItems can always be confident that the items it contains are all red. Now suppose we were able to call 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);
    }
}

Now we could add whatever we like, and the invariant in RedItems is broken.

Does that make sense?

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • 41
    nice example. but i thought it's bad design when the base class accepts items, but the derived class rejects them, because the derived can't be used as a drop-in replacement for the base-class (violation of substitution principle). is that right thinking, or is such a hierarchy clean? – Johannes Schaub - litb Feb 25 '09 at 15:33
  • Thanks for your answer, that's a good point. But that's also the reason why you sometimes should choose encapsulation over inheritance, don't you think? – Tim Büthe Feb 25 '09 at 15:33
  • 5
    Do you mean composition over inheritance? This example isn't a reason to avoid inheritance - although there are plenty of others. – Jon Skeet Feb 25 '09 at 15:34
  • "litb" made a good point and also, nowadays you should write this as an generic, e.g. Item – Tim Büthe Feb 25 '09 at 15:36
  • @Jon, yes, got that wrong. I think I mean the decorator design pattern – Tim Büthe Feb 25 '09 at 15:42
  • 3
    @Tim: This was just an easy-to-understand example of violating encapsulation. It could be setting a property instead. Besides, not all aspects will be visible at a type level. Generics isn't the answer to everything. – Jon Skeet Feb 25 '09 at 15:56
  • 2
    yeah i think i agree with jon too. the superclass can very well say that behavior can be more strict in subclasses - and then the hierarchy is fine i think. – Johannes Schaub - litb Feb 25 '09 at 15:58
  • 2
    I understand that it is not a good design to do so, but I do not think this is reason enough to prevent this in the language. I'm just having great trouble because of this, since I need to fix a bug in RedItems.add and don't have the source for it. But there is no way to fix the bug without using Items.add in NaughtyItems.add. – Hans-Peter Störr May 14 '09 at 12:18
  • 2
    wonder why C++ allows this...different paradigm I guess? http://stackoverflow.com/questions/357307/c-how-to-call-a-parent-class-function-from-derived-class-function – rogerdpack Oct 17 '11 at 18:00
  • 1
    @rogerdpack: *Many* things are different between C++ and Java. However, that question doesn't really show whether you could "skip" a level as is being asked here. – Jon Skeet Oct 17 '11 at 18:18
  • 12
    @JohannesSchaub-litb I think that it violates Liskov substitution principle, since if one codes to the contract of Item and uses an instance of RedItems, one will get unexpected NotRedItemException. I was always taught that a sub class should take a super set of input and return a subset of output. In other words, a sub class should never reject an input that is valid for the super class or produce output that the super class cannot produce, this includes throwing an error that the super class does not throw. – Konstantin Tarashchanskiy Jan 18 '12 at 19:50
  • 3
    Regarding this comment: `Now we could add whatever we like, and the invariant in RedItems is broken.`, this would be true anyway if you don't call `super.add(item);` prior to adding you override code. But I understand your overall point and get why it would be wrong to allow calling `super.super.add(item)` from `add(item)`. – cosjav Feb 17 '14 at 06:07
  • 2
    @cosjav: The invariant of "RedItems won't include any non-RedItems" wouldn't be broken. The contract of "the add method should add an item would be broken" but that's a slightly different contract. That could be fixed by RedItems declaring a final method to prevent future overriding, but we still need to prevent super.super.add from being called in *other* methods. – Jon Skeet Feb 17 '14 at 06:45
  • 1
    We always break encapsulation by overriding inherited methods ! – DayaMoon Apr 09 '14 at 08:27
  • @Jon: "Because inheritance exposes a subclass to details of its parent's implementation, it's often said that 'inheritance breaks encapsulation'". (Gang of Four 1995:19) – DayaMoon Apr 09 '14 at 08:36
  • 2
    @DayaMoon: Inheritance doesn't *always* expose details of a parent's implementation. It might do, but it doesn't *have* to. That sounds like an overstatement to me. – Jon Skeet Apr 09 '14 at 08:37
  • 2
    @DayaMoon: I *would* say that inheritance adds to the *risk* of violating encapsulation, which is why I apply it sparingly, and go along with the idea of "design for inheritance or prohibit it" (with an understanding that designing for inheritance takes a lot of effort). – Jon Skeet Apr 09 '14 at 08:39
  • @Jon Skeet: I agree to all you've said except that: we can't call `super.super.m()` because we break encapsulation (maybe there is some other meaning I don't know). While `super.super.m()` is an obvious example of breaking inheritance: "I like all from my super class except the `m(){}` implementation". – DayaMoon Apr 09 '14 at 08:52
  • 2
    @DayaMoon: The point is that `super.m()` may be performing some action which should *always* take place - you're breaking encapsulation if you can skip that. I'm not sure that this discussion will be productive to be honest - or that SO comments are an appropriate place for it :( – Jon Skeet Apr 09 '14 at 08:54
  • 1
    @JonSkeet where did you read this kind of stuff? any book? I am kind of curious – Govinda Sakhare May 04 '16 at 05:58
  • 5
    @piechuckerr: Sometimes books, sometimes blog posts, sometimes just experience... – Jon Skeet May 04 '16 at 06:37
  • That's some serious hubris on Java's end. If I want to access a feature of a grandparent, it is not the java compiler's place to tell me I can't. Especially when, assuming I have control over the parent class, I can trivially bypass said restriction by just wrapping the grandparent method in a parent method and calling said parent's wrapper method from the child. Saying a Java compiler is justified in throwing an error here is like saying that a C compiler is justified in throwing an error when it catches undefined behavior. This should be a warning, not an error. – Braden Best Mar 18 '19 at 21:19
  • From a *compiler design* perspective, I disagree with this decision. The user should be allowed to design things poorly and shoot themselves in the foot. If you try to restrict that, then they'll find new, even *more* grotesque and convoluted ways to shoot themselves in the foot. Like forking a library to hack one of the classes to bypass said restriction (or worse--forking the compiler to remove the restriction altogether). And then as the official library gets updated over the years, maybe 64-bit support is added, some huge... – Braden Best Mar 18 '19 at 21:32
  • ...optimizations come in, their fork will fall out of date and continue to atrophe. Unless, of course, they re-fork. But then that's a whole 'nother host of problems as now the company has to maintain their "mod" alongside the official release, which is company resources wasted, and it all could have been avoided if the language has just let shitty designers be shitty designers. – Braden Best Mar 18 '19 at 21:34
  • 1
    @BradenBest: This isn't a *compiler design* question at all, and the compiler is entirely correct in what it's doing. It sounds like you have a beef with the *language* design, but that's a different matter. The compiler is *entirely* justified in reporting an error here, and it wouldn't be doing its job if it didn't. As for whether the language design is appropriate - we're going to have to agree to disagree about that, I believe. I'm perfectly happy with the choice here. – Jon Skeet Mar 19 '19 at 07:19
  • @JonSkeet Yep, language design is *exactly* what I was referring to. However, the edit window had expired by the time I noticed the brainfart and I figured I'd worn out my welcome after that three-parter-java-rant :x – Braden Best Mar 20 '19 at 05:56
78

I think Jon Skeet has the correct answer. I'd just like to add that you can access shadowed variables from superclasses of superclasses by casting 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();
        }
}

which produces the output:

x=              3
super.x=        2
((T2)this).x=   2
((T1)this).x=   1
((I)this).x=    0

(example from the JLS)

However, this doesn't work for method calls because method calls are determined based on the runtime type of the object.

Michael Myers
  • 178,094
  • 41
  • 278
  • 290
  • 6
    If you have a variable the same name as one in a superclass and for some reason you can't (or are not allowed to) change it, you can still access the superclass's variable with the same name. What's the use, you ask? Well... I've never used it. – Michael Myers Jun 01 '09 at 12:43
  • Great link to the expressions document. Some nice examples for people studying for the OCPJP. – Gordon Jul 20 '11 at 10:52
  • how come class T1 override variable x ? its static final by default right? – amarnath harish Mar 26 '18 at 17:05
  • @amarnathharish: It's not overridden, and fields are package-protected non-final by default. – Michael Myers Mar 26 '18 at 19:49
  • @MichaelMyers is it so ? then why this question is valid https://stackoverflow.com/questions/1513520/why-are-all-fields-in-an-interface-implicitly-static-and-final – amarnath harish Mar 27 '18 at 11:45
  • @amarnathharish: I misread your previous comment as referring to T2, not T1. You're right about I.x being static and final. But still we're not talking about overriding x, since it is not possible for variables to be overridden. It's just *hidden* or *shadowed*, which means it is difficult to access, as the code example shows. – Michael Myers Mar 27 '18 at 15:34
  • do you know how to access the variable x of T1 and I from T3? it's causing ambiguity while trying to print "x". – amarnath harish Mar 28 '18 at 06:56
43

I think the following code allow to use super.super...super.method() in most case. (even if it's uggly to do that)

In short

  1. create temporary instance of ancestor type
  2. copy values of fields from original object to temporary one
  3. invoke target method on temporary object
  4. copy modified values back to original object

Usage :

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);
    }
}
Pshemo
  • 113,402
  • 22
  • 170
  • 242
Nico
  • 459
  • 4
  • 3
  • 10
    With reflection you can do anything, yes :) You can even make strings mutable. – BalusC May 07 '10 at 14:30
  • 23
    What a horrible thing to do! I'm giving you +1 just for figuring out how to do it at all :) – Larry Watanabe May 18 '10 at 14:06
  • 6
    It's a nice trick but even that is not always equivalent to calling the unavilable, yet needed) super.super and that is because the super.super call would carry the context of C (C+B+A) whereas your answers creates an instance of A without the context of B and C. So this answer will not work if every doThat called getContext(), for example, and getContext was implemented differently in each class. in your answer it would use A's getContext() whereas calling the unavailable super.super would have resulted in using C's getContext. – inor Nov 15 '12 at 09:55
  • Hmm. In some cases, perhaps you could overcome inor's objection with dynamic proxies (http://javahowto.blogspot.co.uk/2011/12/java-dynamic-proxy-example.html), redirecting method calls to the original object (synchronizing variables after each call)? It seems that proxies require everything to be implementing an interface, though. Also, I wonder if it's possible for the super-super class to call one of its super-super methods, specifically, and you'd need NOT to redirect those.... – Erhannis Jul 03 '14 at 03:42
  • I said in another comment that blocking `super.super.` invites programmers to find new, convoluted and egregious ways to shoot themselves in the foot in the pursuit of a workaround, this is a perfect example of that, because your colleagues will probably hate you so much for writing something like this that they will personally and literally shoot you in the foot. +1 – Braden Best Mar 18 '19 at 21:55
12

I don't have enough reputation to comment so I will add this to the other answers.

Jon Skeet answers excellently, with a beautiful example. Matt B has a point: not all superclasses have supers. Your code would break if you called a super of a super that had no super.

Object oriented programming (which Java is) is all about objects, not functions. If you want task oriented programming, choose C++ or something else. If your object doesn't fit in it's super class, then you need to add it to the "grandparent class", create a new class, or find another super it does fit into.

Personally, I have found this limitation to be one of Java's greatest strengths. Code is somewhat rigid compared to other languages I've used, but I always know what to expect. This helps with the "simple and familiar" goal of Java. In my mind, calling super.super is not simple or familiar. Perhaps the developers felt the same?

TM.
  • 94,986
  • 30
  • 119
  • 125
EllaJo
  • 225
  • 1
  • 10
  • 3
    You say "not all superclasses have supers". Well, all but java.lang.Object what could give you "null". So I would say, nearly all have supers. – Tim Büthe Feb 25 '09 at 18:35
  • Every class the application programmer writes has a "super" (java.lang.Object doesn't, but the application programmer doesn't write that.) – finnw Jul 02 '09 at 20:21
  • 2
    Easily solved by making super.super.super...super a compile time error if there are too many supers. Considering Java only has public inheritance, if someone changed the inheritance hierarchy, you are changing the interface. So I would not be not worried that super^n is scary. – Thomas Eding Nov 01 '11 at 19:15
7

There's some good reasons to do this. You might have a subclass which has a method which is implemented incorrectly, but the parent method is implemented correctly. Because it belongs to a third party library, you might be unable/unwilling to change the source. In this case, you want to create a subclass but override one method to call the super.super method.

As shown by some other posters, it is possible to do this through reflection, but it should be possible to do something like

(SuperSuperClass this).theMethod();

I'm dealing with this problem right now - the quick fix is to copy and paste the superclass method into the subsubclass method :)

Larry Watanabe
  • 9,690
  • 9
  • 39
  • 45
  • 1
    Casting before calling a method does not change the method that is delegated to—It's always the subclass' implementation that is used, never the super's. See, e.g., http://stackoverflow.com/questions/1677993/java-inheritance-resolution-in-case-of-instance-methods-and-variables/1678023#1678023 – Joshua Goldberg Jul 19 '12 at 18:21
  • 1
    @Larry this is exactly the situation I was in, and exactly the fix I used. Good call! – bcr Aug 29 '12 at 22:18
  • If you have code of needed method you can open all needed fields and run this code in your subclass. – Enyby Dec 01 '15 at 04:17
6

In addition to the very good points that others have made, I think there's another reason: what if the superclass does not have a superclass?

Since every class naturally extends (at least) Object, super.whatever() will always refer to a method in the superclass. But what if your class only extends Object - what would super.super refer to then? How should that behavior be handled - a compiler error, a NullPointer, etc?

I think the primary reason why this is not allowed is that it violates encapsulation, but this might be a small reason too.

matt b
  • 132,562
  • 64
  • 267
  • 334
4

I think if you overwrite a method and want to all the super-class version of it (like, say for equals), then you virtually always want to call the direct superclass version first, which one will call its superclass version in turn if it wants.

I think it only makes rarely sense (if at all. i can't think of a case where it does) to call some arbitrary superclass' version of a method. I don't know if that is possible at all in Java. It can be done in C++:

this->ReallyTheBase::foo();
Johannes Schaub - litb
  • 466,055
  • 116
  • 851
  • 1,175
  • 6
    It makes sense if someone wrote a subclass with one method implemented incorrectly but the superclass method does 90% of the work. Then you want to make a subclass, and override the method calling the superclass superclass method, and add your own 10%. – Larry Watanabe May 18 '10 at 14:08
3

Look at this Github project, especially the objectHandle variable. This project shows how to actually and accurately call the grandparent method on a grandchild.

Just in case the link gets broken, here is the code:

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";
        }
    }
}

Happy Coding!!!!

kyay10
  • 667
  • 7
  • 12
  • _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_ – Tim Büthe Aug 15 '18 at 20:43
  • Yeah, those are the coder's words, not mine. In my opinion, I think you may actually need it someday – kyay10 Aug 16 '18 at 21:09
3

At a guess, because it's not used that often. The only reason I could see using it is if your direct parent has overridden some functionality and you're trying to restore it back to the original.

Which seems to me to be against OO principles, since the class's direct parent should be more closely related to your class than the grandparent is.

Powerlord
  • 82,184
  • 16
  • 119
  • 164
2

I would put the super.super method body in another method, if possible

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

Or if you cannot change the super-super class, you can try this:

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

In both cases, the

new ChildClass().toString();

results to "I am super super"

xMichal
  • 554
  • 1
  • 6
  • 19
  • I just found myself in a situation where I own the SuperSuperClass and ChildClass but not SuperClass, so I found the first solution useful. – xofon Jan 15 '16 at 18:28
1

It would seem to be possible to at least get the class of the superclass's superclass, though not necessarily the instance of it, using reflection; if this might be useful, please consider the Javadoc at http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()

Dexygen
  • 11,681
  • 11
  • 73
  • 144
1

I have had situations like these when the architecture is to build common functionality in a common CustomBaseClass which implements on behalf of several derived classes. However, we need to circumvent common logic for specific method for a specific derived class. In such cases, we must use a super.super.methodX implementation.

We achieve this by introducing a boolean member in the CustomBaseClass, which can be used to selectively defer custom implementation and yield to default framework implementation where desirable.

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

However, with good architecture principles followed in framework as well as app, we could avoid such situations easily, by using hasA approach, instead of isA approach. But at all times it is not very practical to expect well designed architecture in place, and hence the need to get away from solid design principles and introduce hacks like this. Just my 2 cents...

1

@Jon Skeet Nice explanation. IMO if some one wants to call super.super method then one must be want to ignore the behavior of immediate parent, but want to access the grand parent behavior. This can be achieved through instance Of. As below code

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

Here is driver class,

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

Output of this will be

In C Class
In A Class

Class B printClass behavior will be ignored in this case. I am not sure about is this a ideal or good practice to achieve super.super, but still it is working.

Sanjay Jain
  • 3,308
  • 7
  • 52
  • 88
  • 1
    Well, that's creative but doesn't really answers my question. C still doesn't call super.super, B just behaves differently. If you can change A and B you could just add another method instead of using instanceof. Super.super.foo would help you in cases where you don't have access to A and B and could not change them. – Tim Büthe Sep 04 '13 at 09:54
  • Agree @TimButhe, But if one want to call to super.super then he/she intentionally want to ignore the behavior of the parent class, so u just need to achieve that thing by existing syntax of java. (Any of option you want either instanceof / different method) – Sanjay Jain Sep 05 '13 at 04:11
1
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);

     }
}

run: A BUILD SUCCESSFUL (total time: 0 seconds)

kay
  • 23,543
  • 10
  • 89
  • 128
Boris
  • 19
  • 1
1

Calling of super.super.method() make sense when you can't change code of base class. This often happens when you are extending an existing library.

Ask yourself first, why are you extending that class? If answer is "because I can't change it" then you can create exact package and class in your application, and rewrite naughty method or create delegate:

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() {...}
    ...
}

For instance, you can create org.springframework.test.context.junit4.SpringJUnit4ClassRunner class in your application so this class should be loaded before the real one from jar. Then rewrite methods or constructors.

Attention: This is absolute hack, and it is highly NOT recommended to use but it's WORKING! Using of this approach is dangerous because of possible issues with class loaders. Also this may cause issues each time you will update library that contains overwritten class.

ruruskyi
  • 1,874
  • 2
  • 23
  • 35
0

I think this is a problem that breaks the inheritance agreement.
By extending a class you obey / agree its behavior, features
Whilst when calling super.super.method(), you want to break your own obedience agreement.

You just cannot cherry pick from the super class.

However, there may happen situations when you feel the need to call super.super.method() - usually a bad design sign, in your code or in the code you inherit !
If the super and super super classes cannot be refactored (some legacy code), then opt for composition over inheritance.

Encapsulation breaking is when you @Override some methods by breaking the encapsulated code. The methods designed not to be overridden are marked final.

DayaMoon
  • 327
  • 2
  • 7
0

It is simply easy to do. For instance:

C subclass of B and B subclass of A. Both of three have method methodName() for example.

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

}

Run class C Output will be: Class A Class C

Instead of output: Class A Class B Class C

Shubham Khare
  • 63
  • 1
  • 9
0

In C# you can call a method of any ancestor like this:

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

Also you can do this in Delphi:

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

But in Java you can do such focus only by some gear. One possible way is:

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() result output:

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
  • In C# it will only work for non-virtual methods and since all methods are virtual in Java it isn't really any different. – Agent_L Jan 10 '18 at 11:42
0

The keyword super is just a way to invoke the method in the superclass. In the Java tutorial:https://docs.oracle.com/javase/tutorial/java/IandI/super.html

If your method overrides one of its superclass's methods, you can invoke the overridden method through the use of the keyword super.

Don't believe that it's a reference of the super object!!! No, it's just a keyword to invoke methods in the superclass.

Here is an example:

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

When you call cat.doSth(), the method doSth() in class Animal will print this and it is a cat.

AS Mackay
  • 2,463
  • 9
  • 15
  • 23
qing li
  • 11
  • 1
0

IMO, it's a clean way to achieve super.super.sayYourName() behavior in 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);
    }
}

Output:

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

Yonatan
  • 2,335
  • 2
  • 17
  • 18
Yakov Fain
  • 10,540
  • 4
  • 29
  • 35
  • ah, so you're advocating implementing a class hierarchy like this? Unfortunately, this starts to get really messy if you want to access the method in Mama from Baby (Daughter's subclass)... – bcr Aug 29 '12 at 22:20
  • yakov fain is right. in other words, it's not a good example, because the original question was about calling an overridden method in super.super. – inor Nov 15 '12 at 09:17
0

If you think you are going to be needing the superclass, you could reference it in a variable for that class. For example:

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

Should print out:

2
1
0
Ashtheking
  • 21
  • 4
  • 2
    Your exmaple is kind of... well bad, because you use static methods. When using static methods, you don't need the variable or super at all. Maybe you missed some basic OO concept here, make sure you read up on that. Have to downvote, sorry. – Tim Büthe Jun 06 '11 at 09:08
  • 1
    It could have easily been done without static methods. It's simple enough. I was going for a simple example on how to do this. – Ashtheking Jun 29 '11 at 17:04
  • I get your point, storing the super variable in a field is one way to solve this. But, you don't need the variable if it is a static method, you could just use it. Secondly, invoking static methods one a variable is bad practice and most IDEs will warn you about that. If you fix your answer, removing the static stuff, I'll would be glad to remove my downvote, no offense. – Tim Büthe Jun 30 '11 at 09:12
  • You're close, but your code will not compile. You try to access getNumber from a static context in your main method. Do you actually tried to compile this? (and should'nt your UltraFoo extend SuperFoo?) – Tim Büthe Jul 18 '11 at 14:33
  • I really don't want to be cruel to you, but `new UltraFoo.getNumber()` will not compile, since you missed parentheses there. However, I just removed my donvote, since the concept of your code is pretty clear now, thanks! – Tim Büthe Jul 20 '11 at 14:32
-1
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();
    }
}

Output: Printed in the GrandDad

Andrew
  • 31,284
  • 10
  • 129
  • 99
  • 2
    This answer is outside of the scope of the question. The OP did not ask how to call a method in a grandparent class. The issue is a discussion of why `super.super.method()` is not valid code in Java. – Jed Schaaf May 25 '16 at 15:34