Override a method in Java generically - java

If I have a base class like this that I can't change:
public abstract class A {
public abstract Object get(int i);
}
and I try to extend it with a class B like this:
public class B extends A{
#Override
public String get(int i){
//impl
return "SomeString";
}
}
everything is OK. But my attempt to make it more generic fails if I try:
public class C extends A{
#Override
public <T extends Object> T get(int i){
//impl
return (T)someObj;
}
}
I can't think of any reason why this should be disallowed. In my understanding, the generic type T is bound to an Object—which is the requested return type of A. If I can put String or AnyObject as my return type inside B, why am I not allowed to put <T extends Object> T inside my C class?
Another strange behavior, from my point of view, is that an additional method like this:
public class D extends A{
#Override
public Object get(int i){
//impl
}
public <T extends Object> T get(int i){
//impl
}
}
is also not allowed, with the hint of a DuplicateMethod provided. This one, at least, confuses me, and I think Java should make a decision: if it is the same return type, why not allow overriding; and if it is not, why shouldn't I be able to add this method? To tell me it's the same, but not allow it to be overridden, is very weird, based on common sense.

JLS # 8.4.2. Method Signature
The signature of a method m1 is a subsignature of the signature of a method m2 if either:
m2 has the same signature as m1, or
the signature of m1 is the same as the erasure (§4.6) of the
signature of m2.
As per above rule as your parent do not have an erasure and your child has one so it is not a valid overriding.
JLS#8.4.8.3. Requirements in Overriding and Hiding
Example 8.4.8.3-4. Erasure Affects Overriding
A class cannot have two member methods with the same name and type erasure:
class C<T> {
T id (T x) {...}
}
class D extends C<String> {
Object id(Object x) {...}
}
This is illegal since D.id(Object) is a member of D, C.id(String) is declared in a supertype of D, and:
The two methods have the same name, id
C.id(String) is accessible to D
The signature of D.id(Object) is not a subsignature of that of
C.id(String)
The two methods have the same erasure
Two different methods of a class may not override methods with the same erasure:
class C<T> {
T id(T x) {...}
}
interface I<T> {
T id(T x);
}
class D extends C<String> implements I<Integer> {
public String id(String x) {...}
public Integer id(Integer x) {...}
}
This is also illegal, since D.id(String) is a member of D, D.id(Integer) is declared in D, and:
The two methods have the same name, id
D.id(Integer) is accessible to D
The two methods have different signatures (and neither is a
subsignature of the other)
D.id(String) overrides C.id(String) and D.id(Integer)
overrides I.id(Integer) yet the two overridden methods have the same
erasure
Also It gives example of a case where it is allowed from super to child
The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.
Consider the example:
class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
List toList(Collection c) {...}
}
Now, assume this code was written before the introduction of generics, and now the author of class CollectionConverter decides to generify the code, thus:
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
Without special dispensation, Overrider.toList would no longer override CollectionConverter.toList. Instead, the code would be illegal. This would significantly inhibit the use of generics, since library writers would hesitate to migrate existing code.

Well, for the first part, the answer would be that Java does not allow non-generic methods to be overridden by generic methods, even if the erasure is the same. It means that it wouldn't work even if you would just have the overriding method as:
public <T extends Object> Object get(int i)
I don't know why Java poses this limitation (gave it some thought), I just think it has to do with special cases implemented for sub-classing generic types.
Your second definition would essentially translate to:
public class D extends A{
#Override
public Object get(int i){
//impl
}
public Object get(int i){
//impl
}
}
which is obviously a problem.

From the JLS section 8.4.8.3 Overriding and hiding:
It is a compile-time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold:
m1 and m2 have the same name.
m2 is accessible from T.
The signature of m1 is not a subsignature (§8.4.2) of the signature of m2.
The signature of m1 or some method m1 overrides (directly or indirectly) has the same erasure as the signature of m2 or some method m2 overrides (directly or indirectly).
1 and 2 holds.
3 holds too because (quote from JLS section 8.4.2):
The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method.
And you are having the other way: a method with generic type overriding one without generic.
4 holds too because the erased signatures are the same: public Object get(int i)

Imagine the following invocation.
A a = new C();
a.get(0);
In effect you are calling a generic method, yet you're not passing in any type arguments. As things stand, this is not much of a problem. Those type arguments disappear during code generation anyways. Yet reification has never been taken off the table and the stewards of Java the language have tried and continue to try and keep that door open. If type arguments were reified, your invocation would not provide any to a method that requires one.

#RafaelT's original code results in this compilation error...
C.java:3: error: C is not abstract and does not override abstract method get(int) in A
public class C extends A {
^
C.java:5: error: name clash: <T>get(int) in C and get(int) in A have the same erasure, yet neither overrides the other
public <T extends Object> T get ( int i ){
^
where T is a type-variable:
T extends Object declared in method <T>get(int)
The simplest, most straightfoward solution to get #RafaelT's C subclass to compile successfully, is...
public abstract class A {
public abstract Object get(int i);
}
public class C<T> extends A {
#Override
public T get(int i){
//impl
return (T)someObj;
}
}
Although I'm sure other people meant well with their answers. Nevertheless, a few of the answers seem to have majorly misinterpreted the JLS.
The above change to only the class declaration, results in a successful compilation. That fact alone, means that #RafaelT's original signatures are indeed perfectly fine as subsignatures — contrary to what others have suggested.
I'm not going to make the same mistake that others who answered seem to have made, and try to pretend I fully grok the JLS generics documentation. I confess that I haven't figured out with 100% certainty, the root cause of the OP's original compilation failure.
But I suspect it has something to do with an unfortunate combination of the OP's use of Object as the return type on top of the typical confusing and quirky subtleties of Java's generics.

You should read up on erasure.
In your example:
public class D extends A{
#Override
public Object get(int i){
//impl
}
public <T extends Object> T get(int i){
//impl
}
}
The compiler generated byte code for your generic method will be identical to your non-generic method; that is, T will be replaced with the upper bound, that being Object. That's why you're getting the DuplicateMethod warning in your IDE.

Declaring method as <T extends Object> T get(int i) makes no sense without declaring T somewhere else - in the method's arguments or in a field of the enclosing class. In the latter case, just parameterize the whole class with the type T.

There's a conceptual problem. Suppose C#get() overrides A#get()
A a = new C();
Object obj = a.get(); // actually calling C#get()
but C#get() requires a T - what should T be? There is no way to determine.
You can protest that T is not required due to erasure. That is correct today. However erasure was considered a "temporary" workaround. The type system in most part does not assume erasure; it is actually carefully designed so that it can be made fully "reifiable", i.e. without erasure, in future version of java without breaking existing code.

Related

Can a method be overloaded with another which returns a subclass?

I know that a method can be overriden with one that returns a subclass.
Can it also be overloaded by a method which returns a subclass?
public Object foo() {...}
public String foo(int a) {...}
Is the above code valid?(if placed within a Class)
What about?
public Object foo() {...}
public String foo() {...}
Beginning with Java 5, covariant return types are allowed for overridden methods. This means that an overridden method in a subclass is allowed to use a signature which returns a type that may be a subclass of the parent signature's return type.
To make this concrete, say you have this interface:
public interface MyInterface {
Number apply(double op1, double op2);
:
:
}
The following is legal because the return type is a subclass of Number:
public class MyClass implements MyInterface {
:
:
#Override
public Integer apply(double op1, double op2) {
:
:
return Integer.valueOf(result);
}
}
Because overloaded methods (which have the same name but different signatures) are effectively different methods, you are free to use different return types if you like ... however ... this is discouraged because methods that have the same name but different return types can be confusing to programmers and can complicate the use of your API. It's best not to overload mrthods when you can reasonably avoid it.
This example:
public Object foo() {...}
public String foo(int a) {...}
as long as the two methods get different set of variables, there's no problem with returning different types (even if they are not subclass).
The logic is very simple - if the compiler can choose without doubt which one to use - there's no issue. In this case- if you give the method int it's one method, and without parameters it's the other, no dilema (and the same name does not matter here)
as for:
public Object foo() {...}
public String foo() {...}
This one is not valid, since here the compiler can't 'choose' which one to use.
Yes, and it does not even need to be a subclass foo(), and foo(int) are two completely different and unrelated functions as far as the compiler is concerned, each can have any return type you want.
yeah, you overroad foo with foo(int a) , and gave it a new data type String , the compiler see this as a valid code but the other one Object foo() and String foo() is totally invalid in java

Different return value types in implementation of generic methods

Today I stumbled upon some working Java code I wouldn't even have expected to compile. Reduced to its bare minimum, it looks like this:
import java.util.List;
interface A {
<T> List<String> foo();
}
interface B {
<T> List<Integer> foo();
}
class C implements A, B {
#Override
public List<?> foo()
{
return null;
}
}
At first sight, the type parameter <T> of the foo methods in A and B look unnecessary since T is not used anywhere else. Anyway, I found out that this is playing a crucial role in allowing the conflicting return value types to coexist in the same implementation: if one or both of the <T>s are left out, the code doesn't compile. Here the non-working version:
import java.util.List;
interface A {
List<String> foo();
}
interface B {
List<Integer> foo();
}
class C implements A, B {
#Override
public List<?> foo()
{
return null;
}
}
I don't need to fix the code snippets above as those are just examples I made up to explain my point. I'm only curious to understand why the compiler is behaving differently with them. Can someone explain what rules exactly are making the difference here?
While the first example does compile, it will give an unchecked conversion warning:
// Type safety: The return type List<?> for foo() from the type C needs
// unchecked conversion to conform to List<String>
public List<?> foo()
{
return null;
}
What's happening here is that by declaring type parameters, A.foo() and B.foo() are generic methods. Then, the overriding C.foo() omits that type parameter. This is similar to using a raw type, essentially "opting out" of generic type checking for that method signature. That causes the compiler to use the inherited methods' erasures instead: List<String> foo() and List<Integer> foo() both become List foo(), which can therefore be implemented by C.foo().
You can see that by keeping the type parameter in the C.foo() declaration, there will be the expected compiler error instead:
// The return type is incompatible with A.foo()
public <T> List<?> foo()
{
return null;
}
Likewise, if either of the interface methods don't declare a type parameter, then omitting a type parameter from the override fails to "opt out" of generic type checking for that method, and the return type List<?> remains incompatible.
This behavior is covered in JLS §8.4.2:
The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.
Angelika Langer's generics FAQ expands on this behavior in her section Can a non-generic method override a generic one?:
Now, let us explore an example where non-generic subtype methods
override generic supertype methods. Non-generic subtype methods are
considered overriding versions of the generic supertype methods if the
signatures' erasures are identical.
Example (of non-generic subtype methods overriding generic supertype
methods):
class Super {
public <T> void set( T arg) { ... }
public <T> T get() { ... }
}
class Sub extends Super {
public void set( Object arg) { ... } // overrides
public Object get() { ... } // overrides with unchecked warning
}
warning: get() in Sub overrides <T>get() in Super;
return type requires unchecked conversion
found : Object
required: T
public Object get() {
Here the subtype methods have signatures, namely set(Object) and get()
, that are identical to the erasures of the supertype methods. These
type-erased signatures are considered override-equivalent.
There is one blemish in the case of the get method: we receive an
unchecked warning because the return types are not really compatible.
The return type of the subtype method get is Object , the return type
of the supertype method get is an unbounded type parameter. The
subtype method's return type is neither identical to the supertype
method's return type nor is it a subtype thereof; in both situations
the compiler would happily accept the return types as compatible.
Instead, the subtype method's return type Object is convertible to the
supertype method's return type by means of an unchecked conversion.
An unchecked warning indicates that a type check is necessary that
neither the compiler nor the virtual machine can perform. In other
words, the unchecked operation is not type-safe. In case of the
convertible return types someone would have to make sure that the
subtype method's return value is type-compatible to the supertype
method's return type, but nobody except the programmer can ensure
this.

Java: Overriding an abstract method in subclass

I really should know this, but for some reason I don't understand the following.
My abstract class contains the following abstract method:
protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);
I also have another class as follows:
EvaluationRuleDTO extends from RuleDTO
Then in a subclass of my abstract class I have the following implementation which is not allowed due to "must override or implement a supertype method":
protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {
However, the following is allowed:
protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {
I realize this is probably a basic question but I am a little bemused. How come I can I can return a subclass of RuleDTO in the overridden method, but I can't pass in a subclass?
Thanks
You're breaking the Liskov principle: everything a superclass can do, a subclass must be able to do. The superclass declares a method accepting any kind of RuleDTO. But in your subclass, you only accept instances of EvaluationRuleDTO. What would happen if you did the following?
RuleDTO rule = new EvaluationRuleDTO();
rule.createRowToBeCloned(new RuleDTO());
An EvaluationRuleDTO is a RuleDTO, so it must fulfill the contract defined by RuleDTO.
The method in the subclass may return an instance of EvaluationRuleDTO instead of a RuleDTO, though, because the contract is to return a RuleDTO, and EvaluationRuleDTO is a RuleDTO.
Java allows return type covariance for overrides, so you can specify the return type of an override as a more-derived type. However, Java does not allow parameter type covariance for overrides.
The reason the former is safe is that the object you return will have, at minimum, the functionality of the less-derived type, so a client relying on that fact will still be able to utilize the returned object correctly.
That's not the case for the arguments, though. If it were legal, a user could call the abstract method and pass in a less-derived type (since that's the type declared on the abstract class,) but then your derived override might try to access the argument as a more-derived type (which it isn't) resulting in an error.
In theory, Java could have allowed parameter-type contra-variance, since that is type-safe: if the overriden method only expects a less-derived argument, you can't accidentally utilize a method or field that's not there. Unfortunately, that is not currently available.
It is because when you override a method, you MUST use as parameter type the same type or a more general (wider) type, never a narrower type.
Just think about it. If you could override a method using a narrower type, you would break the polymorphism capability, don't you agree? So, doing this, you would break the Liskov Substitution Principle as JB Nizet said in his answer.
Java 1.5 has co-variant return types which why it is valid
 
The subclass method's return type R2 may be different from superclass
method's return type R1, but R2 should be a subtype of R1. i.e.,
subclass can return type may be a subtype of superclass return type.
In early java that was not the case, but it was changed in Java 5.0.
You cannot have two methods in the same class with signatures that only differ by return type. Until the J2SE 5.0 release, it was also true that a class could not override the return type of the methods it inherits from a superclass. In this tip you will learn about a new feature in J2SE 5.0 that allows covariant return types. What this means is that a method in a subclass may return an object whose type is a subclass of the type returned by the method with the same signature in the superclass. This feature removes the need for excessive type checking and casting.
Source: http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html
This implies that the return types of the overriding methods will be subtypes of the return type of the overridden method.
Please see the following code:
class A {
A foo(A a) {
return new A();
}
}
class B extends A {
#Override
// Returning a subtype in the overriding method is fine,
// but using a subtype in the argument list is NOT fine!
B foo(B b) {
b.bar();
return new B();
}
void bar() {
// B specific method!
}
}
Yes ok, B IS AN A, but what happens if someone does:
B b = new B();
b.foo(new A());
A does not have a bar method.. This is why the argument can not be a subtype of the type of argument in the method being overridden.
Returning A or B in the overriding method is fine. The following snippet will compile and run just fine..
class A {
A foo(A a) {
return new B(); // B IS AN A so I can return B!
}
}
class B extends A {
#Override
B foo(A b) {
return new B(); // Overridden method returns A and
// B IS AN A so I can return B!
}
public static void main(String[] args) {
A b = new B();
final A foo = b.foo(new B());
// I can even cast foo to B!
B cast = (B) foo;
}
}

Generic method with parameters vs. non-generic method with wildcards

According to this entry in the Java Generics FAQ, there are some circumstances where a generic method has no equivalent non-generic method that uses wildcard types. According to that answer,
If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it.
They give the example of a method <T> void print1( List <Box<T>> list), which "requires a list of boxes of the same type." The wildcard version, void print2( List <Box<?>> list), "accepts a heterogenous list of boxes of different types," and thus is not equivalent.
How do you interpret the the differences between the following two method signatures:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
Intuitively, it seems like these definitions should be equivalent. However, the call f(ArrayList.class) compiles using the first method, but the call g(ArrayList.class) using the second method results in a compile-time error:
g(java.lang.Class<? extends java.lang.Iterable<?>>) in Test
cannot be applied to (java.lang.Class<java.util.ArrayList>)
Interestingly, both functions can be called with each others' arguments, because the following compiles:
class Test {
<T extends Iterable<?>> void f(Class<T> x) {
g(x);
}
void g(Class<? extends Iterable<?>> x) {
f(x);
}
}
Using javap -verbose Test, I can see that f() has the generic signature
<T::Ljava/lang/Iterable<*>;>(Ljava/lang/Class<TT;>;)V;
and g() has the generic signature
(Ljava/lang/Class<+Ljava/lang/Iterable<*>;>;)V;
What explains this behavior? How should I interpret the differences between these methods' signatures?
Well, going by the spec, neither invocation is legal. But why does the first one type check while the second does not?
The difference is in how the methods are checked for applicability (see §15.12.2 and §15.12.2.2 in particular).
For simple, non-generic g to be applicable, the argument Class<ArrayList> would need to be a subtype of Class<? extends Iterable<?>>. That means ? extends Iterable<?> needs to contain ArrayList, written ArrayList <= ? extends Iterable<?>. Rules 4 and 1 can be applied transitively, so that ArrayList needs to be a subtype of Iterable<?>.
Going by §4.10.2 any parameterization C<...> is a (direct) subtype of the raw type C. So ArrayList<?> is a subtype of ArrayList, but not the other way around. Transitively, ArrayList is not a subtype of Iterable<?>.
Thus g is not applicable.
f is generic, for simplicity let us assume the type argument ArrayList is explicitly specified. To test f for applicability, Class<ArrayList> needs to be a subtype of Class<T> [T=ArrayList] = Class<ArrayList>. Since subtyping is reflexisve, that is true.
Also for f to be applicable, the type argument needs to be within its bounds. It is not because, as we've shown above, ArrayList is not a subtype of Iterable<?>.
So why does it compile anyways?
It's a bug. Following a bug report and subsequent fix the JDT compiler explicitly rules out the first case (type argument containment). The second case is still happily ignored, because the JDT considers ArrayList to be a subtype of Iterable<?> (TypeBinding.isCompatibleWith(TypeBinding)).
I don't know why javac behaves the same, but I assume for similar reasons. You will notice that javac does not issue an unchecked warning when assigning a raw ArrayList to an Iterable<?> either.
If the type parameter were a wildcard-parameterized type, then the problem does not occur:
Class<ArrayList<?>> foo = null;
f(foo);
g(foo);
I think this is almost certainly a weird case arising out of the fact that the type of the class literal is Class<ArrayList>, and so the type parameter in this case (ArrayList) is a raw type, and the subtyping relationship between raw ArrayList and wildcard-parameterized ArrayList<?> is complicated.
I haven't read the language specification closely, so I'm not exactly sure why the subtyping works in the explicit type parameter case but not in the wildcard case. It could also very well be a bug.
Guess: The thing representing the first ? (ArrayList) does not 'implement' ArrayList<E> (by virtue of the double nested wildcard). I know this sounds funny but....
Consider (for the original listing):
void g(Class<? extends Iterable<Object> x) {} // Fail
void g(Class<? extends Iterable<?> x) {} // Fail
void g(Class<? extends Iterable x) {} // OK
And
// Compiles
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList<Integer>> d = new ArrayList<ArrayList<Integer>>();
f(d);
g(d);
}
}
This
// Does not compile on g(d)
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList> d = new ArrayList<ArrayList>();
f(d);
g(d);
}
}
These are not quite the same:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
The difference is that g accepts a "Class of unknown that implements Iterable of unknown", but ArrayList<T> is constrained implementing Iterable<T>, not Iterable<?>, so it doesn't match.
To make it clearer, g will accept Foo implements Iterable<?>, but not AraryList<T> implements Iterable<T>.

Overriding with subclass as a parameter and generics: where is it in Java Lang Spec?

I've run into Java code similar to the following:
public interface BaseArg {
}
public class DerivedArg implements BaseArg {
}
public abstract class Base <A extends BaseArg> {
A arg;
void doIt() {
printArg(arg);
}
void printArg(A a) {
System.out.println("Base: " + a);
}
}
public class Derived extends Base<DerivedArg> {
void printArg(DerivedArg a) {
System.out.println("Derived: " + a);
}
public static void main(String[] args) {
Derived d = new Derived();
d.arg = new DerivedArg();
d.doIt();
}
}
(feel free to split it into files and run it).
This code ends up invoking the Derived printArg. I realize it's the only logical thing to do. However, if I perform "erasure" on the generic Base manually, replacing all occurrences of A with BaseArg, the overriding breaks down. I now get the Base's version of printIt.
Seems like "erasure" is not total - somehow printArg(A a) is not the same as printArg(BaseArg a). I can't find any basis for this in the language spec...
What am I missing in the language spec? It's not really important, but it bugs me :) .
Please note that the derived method is invoked. The question is why, considering their erased signatures are not override-equivalent.
When compiling class Derived, the compiler actually emits two methods: The method printArg(DerivedArg), and a synthetic method printArg(BaseArg), which overrides the superclass method in terms even a virtual machine ignorant of type parameters can understand, and delegates to printArg(DerivedArg). You can verify this by throwing an exception in printArt(DerivedArg), while calling it on a reference of type Base, and examining the stack trace:
Exception in thread "main" java.lang.RuntimeException
at Derived.printArg(Test.java:28)
at Derived.printArg(Test.java:1) << synthetic
at Base.doIt(Test.java:14)
at Test.main(Test.java:39)
As for finding this in the Java Language Specification, I first missed it as well, as it is not, as one might expect, specified where overriding or the subsignature relation is discussed, but in "Members and Constructors of Parameterized Types" (§4.5.2), which reveals that formal type parameters of the superclass are syntactically replaced by the actual type parameter in the subclass prior to checking for override equivalence.
That is, override equivalence is not affected by erasure, contrary to popular assumption.
If you do "manual" type erasure, you define the arg instance in BaseArg as type "BaseArg", not type "DerivedArg", so that's resolved to Base's "doIt(BaseArg)" method rather than Derived's "doIt(DerivedArg)" method. If you then alter Derived's method signature to
void printArg( BaseArg a )
from
void printArg(DerivedArg a)
it will print "Derived: arg" as expected.
I believe the behaviour that you encountered is due to the overloading method resolution.
See Java Lang Spec on overloading: link text
And also this wonderful resource on Java Generic regarding the topic.
The printArg in Derived does not override the printArg in Base. In order for it to override, by JLS 8.4.8.1, the overriding method's signature must be a "subsignature" of the overridden method's. And then by JLS 8.4.2, a subsignature must either have the same argument types (which yours doesn't), or its erasure must be the same (which is also not true).
First of all, you can compile the source code in a single file if you get rid of the "public" declarations for all of the classes/interfaces except "Derived".
Second, go ahead and do the type erasure by hand. Here's what I got when I did it:
interface BaseArg {}
class DerivedArg implements BaseArg {}
abstract class Base {
BaseArg arg;
void doIt() {
printArg(arg);
}
void printArg(BaseArg a) {
System.out.println("Base: " + a);
}
}
public class Derived extends Base {
void printArg(BaseArg a) {
System.out.println("Derived: " + a);
}
public static void main(String[] args) {
Derived d = new Derived();
d.arg = new DerivedArg();
d.doIt();
}
}
In the generic version of the code, it may look like methods Derived.printArg and Base.printArg have different signatures. However, if that were the case, then Derived.printArg could never be invoked by doIt. The type-erased version of the code makes it clear that Derived.printArg overrides Base.printArg, so doIt polymorphically calls the right method.
How is printArg in Base defined after your manual erasure ?
void printArg(BaseArg a) {
so, printArg(Derived a) does NOT override it and will not be called.
EDIT:
if you use the Override annotation in Derived, you'll get an error doing the manual erasure.

Categories

Resources