Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic method triggers type safety error - why?

Tags:

java

generics

Whilst playing around with solutions for this question, I came up with the following code, which has some compiler warnings. One warning is:

Type safety: The expression of type Test.EntityCollection needs unchecked conversion to conform to Test.EntityCollection<Test.Entity>

I don't entirely understand why this warning appears. By passing in a Class<M> type and declaring the method returns EntityCollection<M>, why am I not doing enough to convince the (Java 7) compiler that the correct type is being returned?

static class Entity {
}

static class EntityCollection<E extends Entity> {

    private EntityCollection(HashMap<?, E> map) {
    }

    public static <T extends HashMap<?, M>, M extends Entity> EntityCollection<M> getInstance(
            Class<T> mapType, Class<M> entityType)
            throws ReflectiveOperationException {

        T map = mapType.getConstructor().newInstance();
        return new EntityCollection<M>(map);
    }
}

public static void main(String[] args) throws Exception {
    // both compiler warnings are on the line below:
    EntityCollection<Entity> collection = EntityCollection.getInstance(
            LinkedHashMap.class, Entity.class);
}

Bonus points if anyone can improve the code to avoid warnings entirely. I've been staring at it for a while and haven't dreamt up any ways to lessen the warnings.

like image 747
Duncan Jones Avatar asked May 20 '14 13:05

Duncan Jones


People also ask

What is the difference between generic class and generic method?

A generic class or structure can contain nongeneric procedures, and a nongeneric class, structure, or module can contain generic procedures. A generic procedure can use its type parameters in its normal parameter list, in its return type if it has one, and in its procedure code.

What are generic methods?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.


2 Answers

The problem is that getInstance is a generic method but you don't pass generic type parameters to it. You can get around it by passing them like this:

    public static void main(String[] args) throws Exception {
        EntityCollection<Entity> collection = EntityCollection.<LinkedHashMap, Entity>getInstance(
                LinkedHashMap.class, Entity.class);
    }

You will still have to deal a rawtypes warning because LinkedHashMap is a generic type. This is problematic in your case since there is a wildcard in the key type.

You face several problems here:

You can't pass parameterized class objects like this: LinkedHashMap<Object, Entity>.class so you pretty much stuck with the rawtypes warning.

like image 135
Adam Arold Avatar answered Sep 19 '22 14:09

Adam Arold


The problem there is T. You are adding a constraint to your method saying that T should extends HashMap<?, M>. However, the way you are later referencing to T is like a generic parameter of the type Class (Class<T>). LinkedHashMap.class is of type Class<LinkedHashMap> not Class<LinkedHashmap<?, Entity>> (which is what you needed)

A Class object always references a non-parameterized type, and that makes sense. Because the generic binding exists in compile-time, and you are going to use that Class to dynamically reflect the state and behaviour of an instance in runtime. Long story short, you can use a Class<HashMap> to build a new instance, not bounded to any type.

So, I guess what you need to do to your code to modify that constraint in the way it looks like:

public static <T extends HashMap, M extends Entity> EntityCollection<M> getInstance(
            Class<T> mapType, Class<M> entityType)
            throws ReflectiveOperationException {

        T map = mapType.getConstructor().newInstance();
        return new EntityCollection<M>(map);
}
like image 21
Claudio Avatar answered Sep 17 '22 14:09

Claudio