This FAQ entry discusses whether and when methods of a generic subtype can override methods of a generic supertype. Method signatures in the sub- and the supertype may involve type parameters of the respective enclosing class. What is the required relationship between type variables in sub- and supertype methods in order to allow for method overriding? The answer is: Yes, methods of a generic subtype can override methods of a generic supertype, but it is not always trivial to get it right and it is common that mistakes are made and that overriding is confused with overloading. Let us start with a simple example: Example (of a generic subtype with override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub <S> extends Super <S> {

...

public void set( S arg) { ... } // overrides

public S get() { ... } // overrides

} Super<S> in the example, where S is the subtype's type parameter. Since the names of type parameters are irrelevant for the method signatures, the corresponding methods in super- and subtype have identical signatures, namely set($T1_extends_Object) and get() and identical return types. The remainder of this FAQ entry discusses slightly more complex overriding situation for further illustration of the principle.

Here is another example of successful overriding. Example (of a generic subtype with override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub< A , B > extends Super< A > {

...

public void set( A arg) { ... } // overrides

public A get() { ... } // overrides

} B simply does not appear in the overriding methods' signatures and is therefore irrelevant for overriding.

However, if we slightly change the subtype, our attempt to override the inherited methods goes wrong. We declare the subtype methods using the type parameter B although we derive from the supertype instantiation on type parameter A . Example (of a generic subtype without override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub< A , B > extends Super< A > {

...

public void set( B arg) { ... } // error: same erasure

public B get() { ... } // error: incompatible return type

}

error: name clash: set(B) in Sub<A,B> and set(T) in Super<A> have the same erasure,

yet neither overrides the other

class Sub<A,B> extends Super<A> {

^

error: get() in Sub cannot override get() in Super;

attempting to use incompatible return type

found : B

required: A

public B get() { ... }

^ set methods have signatures that are no longer override-equivalent, namely set($T1_extends_Object) in the supertype and set($T2_extends_Object) in the subtype. This is because the compiler distinguishes between different type parameters. At the same time, both signatures have the identical erasures, namely set(Object) , and the compiler rejects the subtype method set with an error message because no two methods in the same type may have identical signatures. The get methods have identical signatures, but incompatible return types. Again, because the compiler distinguishes between different type parameters. For this reason the subtpye method get is rejected with an error message.

Let us modify the subtype a second type and see what happens now. The modification is that the second type parameter is bounded by the first type parameter. Example (of a generic subtype without override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub< A , B extends A > extends Super <A> {

...

public void set( B arg) { ... } // error: same erasure

public B get() { ... } // overrides

}

error: name clash: set(B) in Sub<A,B> and set(T) in Super<A> have the same erasure,

yet neither overrides the other

class Sub<A,B extends A> extends Super<A> {

^

The set methods still have signatures that are not override-equivalent, namely set($T1_extends_Object) in the supertype and set($T2_extends_$T1) in the subtype. And both signatures still have the same erasure, namely set(Object) . Again, the compiler rejects the subtype method set with an error message.

The get methods have identical signatures and this time compatible return types, because the type parameter B is a subtype of the type parameter A . For this reason the subtpye method get overrides the supertype method.

Let us consider a situation where the subtype's type parameter has different bounds than the supertype's type parameter. Example (of a generic subtype with override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub< N extends Number > extends Super< N > {

...

public void set( N arg) { ... } // overrides

public N get() { ... } // overrides

} set($T1_extends_Number) with return type void and get() with return type $T1_extends_Number . Let us change the subtype declaration slightly; we declare subclass methods so that they use the bound in the method signatures instead of the type parameter . Example (of a generic subtype with different yet override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub <N extends Number > extends Super< N > {

...

public void set( Number arg) { ... } // overrides

public Number get() { ... } // error: incompatible return type

} set methods now have different signatures, namely set($T1_extends_Number) in the supertype and set(Number) in the subtype. Normally, this would be overloading, but in this case with type parameters involved the signatures are considered override-equivalent. This is because the supertype's set method takes an argument of type $T1_extends_Number , which is a placeholder for a subtype of Number . The subtype's set method takes an argument of type Number , which means that any argument passed to the set method via a supertype reference is acceptable to the subtype version of the method. Consider the example of a concrete parameterization of our super- and subtype in order to understand why it makes sense that the subtype version of the set method is an overriding rather than an overloading version of the supertype method, despite of the different signature. For illustration we use Sub<Integer> and its supertype Super<Integer> . The two methods involved in overriding are Super<Integer>.set(Integer) and Sub<Integer>.set(Number) . When a supertype reference refers to a subtype object and we invoke the set method through the supertype reference, then we see Super <Integer>.set( Integer ) , but actually invoke the overriding Sub <Integer>.set( Number ) . Is it type-safe? Example (of calling the overridden set method): Super<Integer> ref = new Sub<Integer>();

ref.set(10); // calls Sub<Integer>.set(Number) Super<Integer>.set(Integer) are objects of type Integer ; they are handed over to Sub<Integer>.set(Number) , which happily accepts the Integer objects because they are subtypes of Number . And the same is true for all parameterizations of type Sub . because the declared argument type of the supertype method is always a subtype of the declared argument type of the subtype method. Hence it is type-safe that the subtype version of the set method is considered an overriding version of the supertype's set method. The opposite is true for the get methods. They have identical signatures, but incompatible return types, namely $T1_extends_Number in the supertype and Number in the subtype. If we invoked the subtype's set method via a supertype reference then we would expect a return value of type $T1_extends_Number , which is a placeholder for a subtype of Number , while in fact the subtype method would return a Number reference, which can refer to any arbitrary subtype of Number , not necessarily the one we are prepared to receive. Hence considering the subtype version of the get method an overriding version of the supertype's get method would not be type-safe and is therefore rejected as an error.

In constrast, the following fairly similar example leads to an overloading situation. Example (of a generic subtype without override-equivalent methods): class Super <T> {

...

public void set( T arg) { ... }

public T get() { ... }

}

class Sub< N extends Number > extends Super< Number > {

...

public void set( N arg) { ... } // overloads

public N get() { ... } // overrides

} set methods again have different yet similar signatures. This time it is the other way round: we have set(Number) in the supertype and set($T1_extends_Number) in the subtype. This is overlaoding rather than overriding, because the supertype method has a declared argument type that is a supertype of the subtype method's declared argument type. Overriding would not be type-safe in this situation. To understand it let us use the same concrete parameterization as above, namely Sub<Integer> and its supertype Super<Number> . The two methods in question are Super<Number>.set(Number) and Sub<Integer>.set(Integer) . When a supertype reference refers to a subtype object and we invoke the set method through the supertype reference, then we see Super <Number>.set( Number ) , but would actually invoke Sub <Integer>.set( Integer ) , if this were overrding. This is not type-safe because we could pass an object of type Long to Super<Number>.set(Number) , but the subtype method Sub<Integer>.set(Integer) cannot take it. The get methods are not problematic in this example. They have the identical signatures and covariant return type, namely Number in the supertype and $T1_extends_Number in the subtype. Hence, we have overriding for the get methods. Overloading leads to confusing and/or ambiguous method invocations. Consider for example the instantiation Sub<Number> . In this instantiation we have Super<Number>.set(Number) in the supertype and overloaded version Sub<Number>.set(Number) in the subtype, both methods have the same signature. Example (of calling the overloaded set method): Integer integer = 10;

Number number = integer; Sub<Number> ref = new Sub<Number>();

ref.set(integer); // error: ambiguous

ref.set(number); // error: ambiguous error: reference to set is ambiguous,

both method set(T) in Super<Number> and method set(N) in Sub<Number> match

ref.set(integer);

^

eror: reference to set is ambiguous,

both method set(T) in Super<Number> and method set(N) in Sub<Number> match

ref.set(number);

^ Sub<Number> does not override the set method inherited from Super<Number> although in this particular instantiation both methods have the same argument type. This is because the decision whether a subtype method overrides or overloads a supertype method is made by the compiler when it compiles the generic subtype. At that point in time there is no knowledge regarding the concrete type by which the type parameter N might later be replaced. Based on the declaration of the generic subtype the two set methods have different signatures, namely set(Number) in the supertype and set($T1_extends_Number) in the generic subtype. In a certain instantiation of the subtype, namely in Sub<Number> , the type parameter N (or $T1_extends_Number ) might be replaced by the concrete type Number . As a result both set methods of Sub<Number> suddenly have the same arguments type. But that does not change the fact that the two methods still have different signatures and therefore overload rather than override each other.

