Skip to content

Why can’t I add a type parameter to an overriding method?

I need to implement a method from a class Foo, in my subclass Bar:

class Foo {
    public abstract void foo();
}

class Bar extends Foo {
    
    private <T> Map<T, List<T>> getStuff() { ... }

    @Override
    public void foo() {
        Map<?, List<?>> stuff = getStuff();
        for (Entry<?, List<?>> e : stuff.entrySet()) {
            Object key = e.getKey();
            List<?> lst= e.getValue();
            lst.add(key);  // illegal: need ?, got Object
        }
    }
}

As you can see, my method would benefit from a type parameter:

@Override
public <T> void foo() {
    Map<T, List<T>> stuff = getStuff();
    for (Entry<T, List<T>> e : stuff.entrySet()) {
        T key = e.getKey();
        List<T> lst= e.getValue();
        lst.add(key);  // legal!
    }
}

(I could also use raw types because I know it’ll work, but everything I’ve read says you really shouldn’t do that)

Unfortunately, this is illegal – my method foo in Bar “clashes with foo() in Foo; both methods have the same erasure, yet neither overrides the other”.

Which seems odd, given that my type parameter does not alter the parameters or return type of foo – any place it is okay to use Foo.foo in, it is also okay to use my (illegal) Bar.foo in.

My ugly hack of a workaround has, up until this point, been to just write a whole new (parameterized) method:

public void foo() {
    realFoo();
}

private <T> void realFoo() {
    // parameterized version of .foo() above
}

I have two questions:

  1. Why does adding a type parameter to a method prevent it from overriding an unparameterized method in its superclass?
  2. Is there a better way to get the behavior I want?

Answer

See JLS 8.4.2:

8.4.2. Method Signature

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.

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.

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

Because type parameters are part of the method signature, you can’t add or remove type parameters in overriding methods.

My ugly hack of a workaround…

Yes, it’s ugly, but this is the right way to do it.