mercredi 16 août 2023

How do I get the element type of a collection passed in a method?

Suppose I have some method that accepts a List<?>

public void method(List<?> list) {
    // some logic
}

How can I get the element type in runtime? Is it possible?

I found these suggestions (here on SO):

  1. ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0] (doesn't work, List is an interface and doesn't have a superclass)
// ChatGPT's work

public class App {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        List<Integer> integerList = new ArrayList<>();

        Type stringType = getListElementType(stringList);
        Type integerType = getListElementType(integerList);

        System.out.println("String List Element Type: " + stringType.getTypeName());
        System.out.println("Integer List Element Type: " + integerType.getTypeName());
    }

    private static Type getListElementType(List<?> list) {
        Type genericType = list.getClass().getGenericSuperclass();

        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            return parameterizedType.getActualTypeArguments()[0];
        }

        throw new IllegalArgumentException("Unable to determine list element type.");
    }
}

Console output:

String List Element Type: E
Integer List Element Type: E
  1. Storing a corresponding class as a field during creation of a generic class instance (doesn't work, List is read-only, and it can't have any instance fields anyway since it's an interface)
/*
 from jqno's answer to "How can I learn actual type argument of an generic class?"
 Henning in his answer to "Get generic type of class at runtime" suggested a similar trick
*/

class ParameterizedClass<T> {
    private Class<T> type;

    /** Factory method */
    public static <T> ParameterizedClass<T> of(Class<T> type) {
        return new ParameterizedClass<T>(type);
    }

    /** Private constructor; use the factory method instead */
    private ParameterizedClass(Class<T> type) {
        this.type = type;
    }

    // Do something useful with type
}
  1. The TypeTools library. I still need some supertype

The TypeResolver class provides some of the following methods:

Type reify(Type type, Class<S> context)

Returns a fully reified type using type variable information from the context.

Type reify(Type genericType)

Returns a fully reified genericType using information from the generic declaration.

Class<?>[] resolveRawArguments(Class<T> type, Class<S> subType)

Resolves the raw arguments for a type using type variable information from a subType.

Class<?> resolveRawArgument(Class<T> type, Class<S> subType)

Resolves the raw argument for a type using type variable information from a subType.

Type resolveGenericType(Class<?> type, Type subType)

Resolves the generic type using type variable information from a subType.

Class<?> resolveRawClass(Type genericType, Class<?> subType)

Resolves the raw class for a genericType using type variable information from a subType.





Aucun commentaire:

Enregistrer un commentaire