Let's say I have this base class:
abstract public class Base {
abstract public Map save();
abstract public void load(Map data);
}
to my surprise I could do this in a derived class:
public class Derived extends Base {
@Override
public Map<String, String> save() { //Works
...
}
...
}
but I couldn't do this:
public class Derived extends Base {
@Override
public void load(Map<String, String> data) { // Fails
...
}
...
}
What is happening here? Why can I use a specialized return type but not a specialized parameter type?
What's even more confusing is that if I keep the original declaration of load
, I can just assign it to the more special type:
public class Derived extends Base {
@Override
public void load(Map data) {
Map<String, String> myData = data; // Works without further casting
...
}
...
}
-
You can't change the parameters to
load(Map<String, String> data)
because you could easily violate this if you used the base class instead of the specialized class like so:Base base = new Derived() base.load(new HashMap<Integer, Integer>());
This would call the load-method of derived but in violation to the declaration. Because generics are only checked at compile-time it wouldn't be possible to detect that error.
Return values are no problem as long as they are more specialized in subclasses than in superclasses.
-
There's an implicit conversion from the specialized type to the raw type - that's always "safe" because someone using the raw type can't make any assumptions. So someone expecting to get a raw
Map
back from a method doesn't mind if they get aMap<String, String>
.There isn't an implicit conversion from the raw type to the specialized type - if someone passes a raw
Map
intoload
it may have non-string keys and values. That's completely legal by the base type declaration ofload
.Leaving generics aside, your methods are a bit like this:
public abstract class Base { public abstract Object save(); public abstract void load(Object x); } public class Derived extends Base { @Override public String save() { ... } // Valid @Override public void load(String x) // Not valid }
With the generics removed, is it clear why the
save
call is okay here, but theload
call isn't? Consider this:Base b = new Derived(); Object x = b.save(); // Fine - it might return a string b.load (new Integer(0)); // Has to compile - but the override wouldn't work!
Jon Skeet : Downvoters... please leave comments. -
I believe the issue is that in general it is possible to have two methods that discriminated by argument types. Precisely one needs to be selected. This requires looking at unerased types. This means that the two
load
methods are distinguishable. So we have distinguishable methods but in erased form only one method exists.This is not a problem with return types because (in Java) you can have covariant return types but not overloading by return types.
Jon Skeet : Would the issue I gave in my answer also not be a problem though?Tom Hawtin - tackline : It's irrelevant.
0 comments:
Post a Comment