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?
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.
Related
I have an abstract superclass
public abstact class SuperClass
2 classes extend this superclass
public class ChildClass1 extends SuperClass
public class ChildClass2 extends SuperClass
I have an arraylist
List<SuperClass> objects = new ArrayList<SuperClass>();
I have a method that has 2 different parameter list
public void get(ChildClass1 o)
public void get(ChildClass2 o)
When trying something like this:
for (SuperClass o : objects)
get(o);
I got an error: "The method get() is not applicable for the arguments SuperClass"
How should I implement this in a way that every element in the list could call the right method? Is the only way I can implement this is with instanceof and casting it or is there a nicer way?
This is not possible as Java decides at compile time which one of these methods to use. You could have a get method defined in SuperClass, then the code would look like this:
for (SuperClass o : objects)
o.get();
A quick Stackoverflow search lead me to this thread there is more information for you: overloading and compile time binding. Someone proposed to use the visitor pattern there. That might be an alternative, too. As every Java developer encounters the issue you have asked sooner or later I am sure there are plenty of interesting threads on this topic to be found here on Stackoverflow.
Java binds (most) method invocations dynamically with respect to the object on which they are invoked, but it determines type signatures statically. Thus, when your code ...
for (SuperClass o : objects)
get(o);
... is analyzed at compile time, Java must select a get() method signature based only on the information it has at that time, which is that o is a SuperClass. Moreover, it will choose exactly one method signature, for an existing method on the relevant class. At run time, it will resolve that method signature against the class of the object on which it is invoked, considering other signatures only to the extent needed for supporting covariant return types.
That you have a requirement such as you do creates a distinct code smell. It makes sense to collect objects in a List<SuperClass> only for purposes that are served equally well (and perhaps polymorphically) by any SuperClass, which is not the case for you. If you cannot change that aspect of the design, then there are multiple ways you could proceed, including ...
An ugly hack way
Use instanceof and casting:
for (SuperClass o : objects) {
if (o instanceof ChildClass1) {
get((ChildClass1) o);
} else if (o instanceof ChildClass2) {
get((ChildClass2) o);
} else if (o == null) {
// do something appropriate
} else {
throw new SomeKindOfException(
"Don't know what to do with a " + o.getClass());
}
}
Double dispactch / inversion of control
Give the superclass a method ...
abstract void acceptGetter(ClassThatGets objectThatGets);
... and have each subclass implement it the same way:
void acceptGetter(ClassThatGets objectThatGets) {
objectThatGets.get(this);
}
Then replace your problematic loop with ...
for (SuperClass o : objects)
o.acceptGetter(this);
... where it is assumed that that code appears in class ClassThatGets, which declares the get() methods you described, or in a subclass of that class.
There are surely other alternatives as well, some of which may fit more cleanly into your overall design.
First I want to clarify that I don't have any real business requirements for that, It's a purely theoretical question.
Assuming that we have these two Interfaces :
public interface SuperInterface {
void doSuperStaff();
void doComStaff();
}
public interface SubInterface extends SuperInterface {
// Something here to invalidate doSuperStaff()
}
Is there any way to invalidate / disable the doSuperStaff() for all classes that implement the SubInterface? So only the classes that directly implement SuperInterface can use that method and override it.
NB: I know that we can resolve that in a conceptual manner, but I want to know if there is a way to make that possible technically: using annotations for example ( like the #Deprectaed, which only instructs the compiler that the method is deprecated.)....
Limited, but the following helps a bit.
public interface SubInterface extends SuperInterface {
#Deprecated
void doSuperStaff();
}
Or even
public interface SubInterface extends SuperInterface {
#Deprecated
default void doSuperStaff() {
throw new UnsupportedOperationException("...");
}
}
UPDATE If it's purely theoritical and you are not stuck with a legacy design you can't change, then it's bad idea to try and remove Super interface methods on a sub-interface (or class). It's indicative the design is wrong (Dog -> Animal (useLegs, hasBody), Owl -> Animal (useLegs-??, hasBody). useLegs in this case doesn't belong in Animal.
But if you have legacy code you want to remove it from, and enforce it with Compile-time errors, this approach will work.
You can remove the method with Aspects. Create a pointcut that triggers when the method is called, add Around Advice which is triggered on that pointcut and throws an unsupported operation exception rather than proceed.
With AspectJ Spring syntax it looks something like this (Syntax details here -http://www.eclipse.org/aspectj/doc/next/adk15notebook/ataspectj-pcadvice.html)
#Around("call(* SubInterface.doSuperStuff())")
public Object removeMethod(ProceedingJoinPoint thisJoinPoint) {
throw new UnsupportedOperationException("...");
}
You can also use the native AspectJ language and it's Eclipse plugin to have Eclipse (or your build) show a compile error on the call site if any code attempts to use it. I think that would look something like this (https://eclipse.org/aspectj/doc/released/adk15notebook/annotations-decp.html)
declare error : call(* SubInterface.doSuperStuff())
: "Method doSuperStuff removed from SubInterface";
As you said the answer is NO.
This is simple illustration, why this feature cannot be added.
Firstly the meaning of Interface in Java is Behavior
Suppose if I create a new interface AdavancedComparable<T> which extends java.lang.Comparable<T> and if I deprecate the public int compareTo(T o); in my AdvancedComparable interface and add my own method like advancedCompare(T o);
public interface AdavancedComparable<T> extends Comparable<T> {
// invalidate/deprecate for compareTo method using requested feature somehow
public int advancedCompare(T o);
}
Any method which is accepting Comparable<T> type parameter will also accept AdvancedComparable<T> type parameter, but when the method tries to call compareTo, it will break if the feature you asked is implemented.
Suppose if compiler updated to recognize this issues and made to throw compile time error, that's fine, but it also has to support the legacy code. Remember the Generics case, many classes which altered to support generic type, supports Raw type too (Like List, Class etc). Just to support legacy code. But in our case it is difficult to do so, I feel.
Can not you use super Abstract Class which would implement doSuperStuff() and other concrete class could extend it to override other method.
You can always cast a subtype to a supertype, and invoke methods in the supertype.
However, there is a way to prevent overriding --
public interface SuperInterface
Object doSuperStaff(); // return value doesn't matter, can be null
public interface SubInterface extends SuperInterface
default Foo doSuperStaff() { ... }
Here, Foo is a package-private type. Subclasses of SubInterface (in different packages) have no way to override this method, because they can't access Foo.
It's also possible to use a private class Foo there.
I need to equip my class with polymorphic cloning (deep copy), i.e. I need something like this to work:
SuperType original = new SubType();
SuperType copy = original.clone();
where original.clone() can be substituted by any mechanism to create a deep copy, and the actual type of copy shall be SubType, because original is also a SubType.
Is the clone() method and Cloneable interface the only way to achieve this? Factory methods and copy constructors cannot by used, since the actual class is known only in run time, right? Are there any other suggested methods except those serialize-deserialize approaches, and the Java deep-cloning library which is IMHO even worse black magic than the clone() method?
Thanks, Petr
Actually Object's clone() method is not allowing you to do any polymorphic calling because is protected. Implementing Cloneable is of no use either because it doesn't contain a clone() method.
You can do polymorphic cloning by providing a polymorphic method for the cloned classes that in turn invoke the copy constructor.
abstract class SuperType {
public SuperType(SuperType t) {
}
public abstract SuperType deepCopy();
}
class SomeType extends SuperType {
SomeType(SomeType t) {
//copy constructor
}
#Override
public SomeType deepCopy() {
return new SomeType(this);
}
}
...
SuperType original = new SubType();
SuperType copy = original.deepCopy(); //the deepCopy is called on children classes
See also:
Joshua Block's opinion on cloning vs. copy constructor
In order for your code to work, that code is either inside the SuperType class or subclass, or your SuperType type must have a public clone() method. A public clone() method does not exist automatically, and you must implement it. You could call it anything else for that matter, e.g. copy().
Your question then, is how to implement that clone() (or whatever you call it) method so that it does polymorphic cloning.
The way that was intended by the Java developers is to call super.clone(), assuming that all the classes in the inheritance hierarchy similarly implemented clone() to perform polymorphic cloning. This ultimately gets to the protected Object.clone() method, which does the magic of polymorphic cloning. Note that for Object.clone() to not throw an exception, your class must implement the Cloneable interface.
However, there are other possible ways. For example, assuming that all subclasses have a default constructor, you can do something like this.getClass().newInstance(). This will create an object of the right class, but the fields are not copied over. Your clone method will need to copy all the fields, and subclasses must override your clone method to copy their fields, etc. Note that it is irrelevant whether the Cloneable interface is implemented in this case.
Another way is, assuming that the class is Serializable, to serialize and unserialize this. This should create a polymorphic clone that has all the serializable fields carried over.
You can simply add a copy method to SuperType and implement it in all SubTypes
class SuperType {
public SuperType copy() {
...
}
}
If you don't like the Cloneable interface, you can create your own interface. Each class will be responsible for creating a new instance of the correct type.
Consider the following code from The Java Programming Language book
public class MyClass extends HerClass implements Cloneable {
public MyClass clone()
throws CloneNotSupportedException {
return (MyClass) super.clone();
}
// ...
}
When the overiding clone() function already species the return type as MyClass then what is the requirement of specifying it again in the return statement ?
Also since the clone of Myclass's super class object is being created (cause clone() is being called wrt superclass), how can it be of Myclass type?
Thanks in advance
Because clone() returns an object of class Object, and you must cast it to the correct type. But you know it is an object of type MyClass, so that cast is correct.
In theory you're right: as you have to specify the type of function return values the compiler could try and perform the correction automatically. On the other hand requiring an explicit conversion helps identify possible errors.
Unless you have specific requirements the clone() method of the Object class already does the right thing, i.e. it creates an object of the correct class and copies all the non-static attributes in the cloned object. However it cannot return it as a derived type because at compile time that type is not known to the Object class itself.
It is true that the clone() method could have been provided automatically for all classes, but sometimes you don't want it to be available and at other times you want to override the default behaviour; for instance you might have an id attribute in your class that you want to be different for each instance of your class even when cloned. Having to override the clone() method gives you a place where you can implement such functionality.
This is because the clone() method in Object returns an Object. However you can return your subclass in clone() because it extends an Object. If the method in MyClass looked like this
public Object clone()
Then it would still be a valid cloneable object and it would work. You wouldn't need to cast anything. The interface, Cloneable is just a marker interface, which means it doesn't actually have any methods.
Your easy question first: why is super.clone() cast to MyClass? That's because the declaration of HerClass.clone() specified a returned value of HerClass, so you must cast it to the right type.
Now, for the more difficult question: how can super.clone() actually return an instance of MyClass? I actually had a hard time finding the answer, but I did somewhat find an answer in Effective Java by Joshua Bloch. There is still some "magic" in the background of Object.clone() that I don't quite understand.
Item 11 from the book:
In practice, programmers assume that if they extend a class and invoke
super.clone from the subclass, the returned object will be an instance
of the subclass. The only way a superclass can provide this
functionality is to return an object obtained by calling super.clone.
If a clone method returns an object created by a constructor, it will
have the wrong class. Therefore, if you override the clone method in a
nonfinal class, you should return an object obtained by invoking
super.clone. If all of a class’s superclasses obey this rule, then
invoking super.clone will eventually invoke Object’s clone method,
creating an instance of the right class.
I originally tried to answer your question by writing a program without knowing you always had to call super.clone(). My homemade clone method for HerClass was returning a new instance of HerClass generated from a constructor (new HerClass()). The code compiled, but it failed at execution when I was trying to cast (MyClass) super.clone(). Only methods that are chained down from Object.clone() can return a value that is an instance of one of their subtype.
Note that if HerClass.clone() is not explicitly implemented, by default it simply returns Object.clone(). The default method has protected access, but since you are calling it from a subclass, it's not a problem.
As a practical example of the general question in the subject, I'd like to implement the containsAll method in the Set interface with
public boolean containsAll(Iterable<?> c) { /* ... */ }
I figure this should be allowed, since Collection is Iterable meaning such a containsAll would cover the interface requirement. Likewise, more generally being able to implement interfaces with argument superclasses seems like it should work.
However, Eclipse says no way (haven't tried just javac straight-up) - can someone explain the reason for that? I'm sure there's something in the spec which makes it the way it is, but I'd like to understand the motivation for requirement as well. Or am I missing something like Iterable<?> not being a superclass of Collection<?>?
As a side question - given I'm declaring two methods would the method with the Iterable signature always be preferred on calls with a Collection argument?
Eclipse Error:
If I remove the method with the Collection signature, just leaving the Iterable one (see after error), I get the following:
The type BitPowerSet must implement the inherited abstract method Set<Long>.containsAll(Collection<?>)
The exact implementation being:
#Override public boolean containsAll(Collection<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}
public boolean containsAll(Iterable<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}
Since the interface you are implementing declares the (abstract) method containsAll(Collection<?>), you must implement it with this exact signature. Java does not allow you to implement/override a method with a wider parameter type than the original. This is why you get the error you show when you comment out your method with the Collection signature.
You don't show the other error you claim to get when the method is not commented out, but I guess it might have to do something with ambiguous method overloading.
My guess as to why java has this restriction is, say you have:
class A {
void foo(String s) { ... }
}
class B extends A {
// Note generalized type
#Override void foo(Object s) { ... }
}
Now if you have class C extends B and it wants to override foo, it's not clear what argument it should take.
Say for example C extended A directly at first, overriding void foo(String s), and then it was changed to extend B. In this case C's existing override of foo would become invalid because B's foo should be able to handle all Objects, not just Strings.
The argument types are part of the method signature so the jvm needs a method with exact the same signature to find overrides. A containsAll( Iterable) will have a different signature than containsAll(Collection).
If I remember right the compiler has to use some workarounds to make generics work in spite of this limitation.
To your second question, the compiler would prefer the Collection argument since it is a subtype of Iterable, this makes the Collection method more specific than the Iterable one.