Implementing a raw interface that has other generic definitions in it

162 views Asked by At

Given the following interface and class:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

public class Cls implements Interface {
    @Override
    public List get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

Results in a compile error:

TypeTest.java:[9,11] error: Cls is not abstract and does not override abstract method get(List) in Interface
TypeTest.java:[12,20] error: name clash: get(List<List<Object>>) in Cls and get(List<List<Object>>) in Interface have the same erasure, yet neither overrides the other

But this works:

public interface Interface<T> {
    List<T> get();
}

public class Cls implements Interface {
    @Override
    public List get() {
        return Collections.emptyList();
    }
}

as does this:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

public class Cls implements Interface {
    @Override
    public List get(List keys) {
        return Collections.emptyList();
    }
}

I don't understand this behaviour. The type erasure only applies to the type paramter T, does it not? Why does missing out T affect the unrelated List<List<Object>> keys parameter?

3

There are 3 answers

0
rgettman On BEST ANSWER

You are getting a compiler error in your first example because of the type erasure that occurs with raw types. Section 4.8 of the JLS describes the circumstances under which using a raw type would yield type erasure:

To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type.

and

The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations.

Other clauses in this section also describe how the types of fields, return types of methods, and parameter types of methods are also subject to type erasure.

Moving on to Section 4.6 of the JLS, as referred above:

The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased.

Therefore, the erasure of the method get is

List get(List);

Your initial example will not compile because the method signature for Cls's get method, get(List<List<Object>>) does not match the raw interface's method, and it doesn't implement the raw interface's get method.

That is also why your second and third examples compile -- they properly override the raw interface Interface.

Incidentally, to override the generic Interface interface without resorting to implementing the raw type, supply a type argument in the implements clause:

public interface Interface<T> {
    List<T> get(List<List<Object>> keys);
}

Implementing it:

class Cls<T> implements Interface<T> {
    @Override
    public List<T> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

Or

class Cls implements Interface<String> {
    @Override
    public List<String> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

Any reference type would work in implements Interface<String>, as long as the type matches the return type in get. The clause implements Interface<Integer> would work with the method List<Integer> get(List<List<Object>> keys).

In short, type erasure occurs for all parameterized types and generic types in a raw class/interface, regardless of whether they are related to the generic type parameters declared on class/interface.

0
nik_7 On

What You are trying to do is a little bit tricky when it comes to compilation,for reference see classes Like Hashtable and search for class like KeySet in the same,this may give you some idea.

0
A Paul On

I think the Class Test also have to be Object generic type. Below are the code works.

interface Interface<T> {
    List<T> get(List<List<T>> keys);
}

public class Test<Object> implements Interface<Object> {

    @Override
    public List<Object> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}

And below also

interface Interface<Object> {
    List<Object> get(List<List<Object>> keys);
}

public class Test<Object> implements Interface<Object> {

    @Override
    public List<Object> get(List<List<Object>> keys) {
        return Collections.emptyList();
    }
}