Change the signature of your method:
public static <T> List<T> isTriggeredByBlackList(Map<String, ? extends T> params, Class<T> clz)
Why does this work?
The expression ? extends T
just means that any type that is a subtype of T
(and of course T
itself) is accepted.
So when method isTriggeredByBlackList
is called like that:
isTriggeredByBlackList(map, List.class);
... T
is specified to be a (raw) List
type, and so the first parameter must be a Map<String, any-type-that-extends-raw-List>
, which is true for Map<String, List<String>>
(because List<String>
is a subtype of (raw) List
).
But why is a List a subtype of (raw) List?
Generics are tricky, because polymorphism does not work as expected (on the first sight). A List<String>
is-NOT-a List<Object>
, although String
extends Object
! So this won't work:
List<Object> objList = new ArrayList<String>(); // compile error
(Note: It's good that this is not possible, but that's another story)
But a List<String>
IS-a (raw) List
(and it is-a List<?>
). So this works:
List rawList = new ArrayList<String>(); // just compiler warning
List<?> unknownList = new ArrayList<String>();
The reason is: Raw types are still supported to be backwards-compatible! Otherwise old code that does not support generics could not be used nowadays. So any instance of a concrete parametrized type (e.g. ArrayList<String>
) can be assigned to a reference of its raw type (e.g. ArrayList
) or super types (e.g. List
)!
Why can't we pass something like List<String>.class
?
Because parameterized types have no exact runtime type representation!
Why is there no class literal for concrete parameterized types?
But I want to get a List<List<String>>
as return value!
This is no problem! Just define the left side of your assignment to be a List<List<String>>
, and java does the rest:
List<List<String>> l = isTriggeredByBlackList(map, List.class);
BUT! This only works, if you slightly modify your method declaration:
public static <T> List<T> isTriggeredByBlackList(
Map<String, ? extends T> params, Class<? super T> clz)
(Otherwise you can't pass the raw List.class
as second argument).
If all this modifications and tweaks make sense depends on the task your method should fulfill! Is it only reading from params
? How does it make use of the generic type arguments? What is the advantage of using generics here? etc.
Btw.: Methods that start with is...
should return a boolean
value. Consider renaming your method!
Btw 2.: The return type of your method is List<T>
, so in case you're specifying T
to be a List
, you'll get a List<List>
. Is this intended?