views:

4483

answers:

9

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 to at to 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.

A: 

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
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
+39  A: 

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
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
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
Do you mean composition over inheritance? This example isn't a reason to avoid inheritance - although there are plenty of others.
Jon Skeet
"litb" made a good point and also, nowadays you should write this as an generic, e.g. Item<Red>
Tim Büthe
@Jon, yes, got that wrong. I think I mean the decorator design pattern
Tim Büthe
@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
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
!isRed() and !isBig() ?
toolkit
I wondered the same as toolkit. There should be a negation.
Svante
Fixed, thanks. Not sure why I didn't spot toolkit's comment before :(
Jon Skeet
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.
hstoerr
+1 for `NaughtyItems`!
f1sh
A: 

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.

R. Bemrose
+3  A: 

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
Obviously, it would be a compiler error - and it's information that the compiler does have.
Michael Borgwardt
+4  A: 

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?

EllaJo
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
Every class the application programmer writes has a "super" (java.lang.Object doesn't, but the application programmer doesn't write that.)
finnw
+3  A: 

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
This might work, but what's it useful for?
finnw
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
A: 

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

George Jempty
+2  A: 

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

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);
    }
}
Nico
With reflection you can do anything, yes :) You can even make strings mutable.
BalusC
What a horrible thing to do! I'm giving you +1 just for figuring out how to do it at all :)
Larry Watanabe
A: 

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