2

I came over a strange behavior of the Eclipse compiler today and I'm not sure what to think of it. We're trying to create a useful Cloneable interface like that:

public interface PublicCloneable extends Cloneable {

    Object clone();

    static <T extends PublicCloneable> T clone(final T obj) {
        if (obj != null) {
            return (T) obj.clone();
        }
        return null;
    }
}

The fun part is that the compiler complains about obj.clone(): Unhandled exception type CloneNotSupportedException

I know how to fix it, we can just cast obj to PublicCloneable and be done with it. But what I'm interested in: why would the compiler prefer the method of Object to a method of an implementation?

Stefan S.
  • 3,950
  • 5
  • 25
  • 77
  • 1
    The compiler during compile-time just checks whether the method being called exists in the top-most class in the hierarchy. It doesn't *resolve* calls to actual concrete classes. – TheLostMind Mar 04 '15 at 11:21
  • 1
    @TheLostMind What's the difference? The top-most class is still `PublicCloneable` not `Object`. – Stefan S. Mar 04 '15 at 11:24
  • No. the compiler checks whether the method is defined in one of the superclasses of `PublicCloneable`. During Runtime, the actual call will be resolved to the `clone()` method of the Concrete class. Simple put, the compiler doesn't care whether *Your concrete class* actually ahs that method. all it wants to know is that someone in the class hierarchy of your class should ahve it. The moment it finds it, it says - *Code is fine* ! – TheLostMind Mar 04 '15 at 11:27
  • 1
    @TheLostMind So the compiler resolves it in excactly the opposite direction as the runtime execution? That's crazy! o_O – Stefan S. Mar 04 '15 at 11:33
  • 1
    The compiler just checks for its existance.. Thats all :P – TheLostMind Mar 04 '15 at 11:36
  • @TheLostMind If I change the type of the argument from `T` to `PublicCloneable` it works as expected, but according to that logic, it should find the protected method first as well. – Stefan S. Mar 04 '15 at 11:46

1 Answers1

0

From this answer: https://stackoverflow.com/a/13776045/896588

[...] while the interface itself doesn't extend Object, it is known that any implementation will.

So since you have an interface, the declared clone() doesn't override the clone() of Object. It is formally a newly declared operation at this point.

Try implementing your interface. You will see that it forces you to override clone() from Object but without the exception since it also has to fulfill the interface.

You have created a tricky and complex situation here. The compiler knows that your implementing class will also extend Object and inherit clone() with the exception. When it looks for a declaration in the type hierachy, it finds that one first and stops searching. It doesn't care anymore that PublicCloneable (which will have a clone() without exception) is in the type hierachy too. If you change the parameter type directly to PublicCloneable, that's where it start searching and finds the other one.

This might be explained too simplified but I can't seem to find the corresponding formal parts in the JLS.

Community
  • 1
  • 1
André Stannek
  • 7,773
  • 31
  • 52