How to extract class from Java generic class to satisfy compiler?

Xobotun :

I am fully aware this question was asked many times, but I cannot find an answer to it. :/

I have one parametrized class:

public class MessageType<T> {
    private final Class<T> clazz;

    public MessageType(final Class<T> clazz) {
        this.clazz = clazz;
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

And several static objects of this class:

static final MessageType<String> TYPE_A = new MessageType<>(String.class);
static final MessageType<Double> TYPE_B = new MessageType<>(Double.class);
static final MessageType<List<String>> PROBLEM_TYPE = new MessageType(List.class);

The problem is that I have to omit diamond operator and stick to unchecked cast of MessageType to MessageType<List<String>> in the last line.

I would like to write something like

static final MessageType<List<String>> PROBLEM_TYPE = new MessageType<>(List<String>.class);

but List<String>.class cannot be computed in runtime as, you know, type erasure. :D

➥ Is there any way to comply with the compiler, and avoid unchecked casts? (It has already costed me an hour due to my negligence and lack of attention)

k5_ :

You can't avoid the unchecked cast here; List<String> is not a Class<?> but a ParameterizedType (both extend/implement Type but have no further common parent). You can store and use the type information of List<String> (with an ParameterizedType). But there will always be some unchecked cast to get back to List<String>.

There are some options for workarounds. Most are tailored to the actual usecase.

You can move the unchecked cast into MessageType:

public <T> static MessageType<List<T>> createListMessage(Class<T> innerType){
    return (MessageType<List<T>>) (Object) new MessageType(List.class);
}

If you need Class to create a new Instance. Provide a Supplier<T>.

   private final Supplier<T> supplier;
   public MessageType(Supplier<T> supplier){
       this.supplier = supplier;
   }
   public T newType(){
      return supplier.get();
   }

   MessageType<String> STRINGS = new MessageType(String::new);
   MessageType<List<String>> STRING_LIST = new MessageType(ArrayList::new);

If you need it for serialization a Supertype-Token is an option.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=163652&siteId=1