Como posso fazer esse tipo de coisa funcionar? Posso verificar se, (obj instanceof List<?>)
mas não se (obj instanceof List<MyType>)
. Existe uma maneira de fazer isso?
Como posso fazer esse tipo de coisa funcionar? Posso verificar se, (obj instanceof List<?>)
mas não se (obj instanceof List<MyType>)
. Existe uma maneira de fazer isso?
Respostas:
Isso não é possível porque o tipo de dados é apagado em tempo de compilação dos genéricos. A única maneira possível de fazer isso é escrever algum tipo de wrapper que contenha o tipo que a lista contém:
public class GenericList <T> extends ArrayList<T>
{
private Class<T> genericType;
public GenericList(Class<T> c)
{
this.genericType = c;
}
public Class<T> getGenericType()
{
return genericType;
}
}
if(!myList.isEmpty() && myList.get(0) instanceof MyType){
// MyType object
}
value
which returns Object
, and I need to check - if it's a list, if it is, check if the list type instanceof my interface. No wrapper or parametrized type is useful here.
You probably need to use reflection to get the types of them to check. To get the type of the List: Get generic type of java.util.List
This could be used if you want to check that object
is instance of List<T>
, which is not empty:
if(object instanceof List){
if(((List)object).size()>0 && (((List)object).get(0) instanceof MyObject)){
// The object is of List<MyObject> and is not empty. Do something with it.
}
}
if (list instanceof List && ((List) list).stream()
.noneMatch((o -> !(o instanceof MyType)))) {}
If you are verifying if a reference of a List or Map value of Object is an instance of a Collection, just create an instance of required List and get its class...
Set<Object> setOfIntegers = new HashSet(Arrays.asList(2, 4, 5));
assetThat(setOfIntegers).instanceOf(new ArrayList<Integer>().getClass());
Set<Object> setOfStrings = new HashSet(Arrays.asList("my", "name", "is"));
assetThat(setOfStrings).instanceOf(new ArrayList<String>().getClass());
setOfIntegers
and setOfStrings
?
If this can't be wrapped with generics (@Martijn's answer) it's better to pass it without casting to avoid redundant list iteration (checking the first element's type guarantees nothing). We can cast each element in the piece of code where we iterate the list.
Object attVal = jsonMap.get("attName");
List<Object> ls = new ArrayList<>();
if (attVal instanceof List) {
ls.addAll((List) attVal);
} else {
ls.add(attVal);
}
// far, far away ;)
for (Object item : ls) {
if (item instanceof String) {
System.out.println(item);
} else {
throw new RuntimeException("Wrong class ("+item .getClass()+") of "+item );
}
}
You can use a fake factory to include many methods instead of using instanceof:
public class Message1 implements YourInterface {
List<YourObject1> list;
Message1(List<YourObject1> l) {
list = l;
}
}
public class Message2 implements YourInterface {
List<YourObject2> list;
Message2(List<YourObject2> l) {
list = l;
}
}
public class FactoryMessage {
public static List<YourInterface> getMessage(List<YourObject1> list) {
return (List<YourInterface>) new Message1(list);
}
public static List<YourInterface> getMessage(List<YourObject2> list) {
return (List<YourInterface>) new Message2(list);
}
}
The major concern here is that the collections don't keep the type in the definition. The types are only available in runtime. I came up with a function to test complex collections (it has one constraint though).
Check if the object is an instance of a generic collection. In order to represent a collection,
false
instanceof
evaluationList
or Set
, the type of the list comes next e.g. {List, Integer} for List<Integer>
Map
, the key and value types come next e.g. {Map, String, Integer} for Map<String, Integer>
More complex use cases could be generated using the same rules. For example in order to represent List<Map<String, GenericRecord>>
, it can be called as
Map<String, Integer> map = new HashMap<>();
map.put("S1", 1);
map.put("S2", 2);
List<Map<String, Integer> obj = new ArrayList<>();
obj.add(map);
isInstanceOfGenericCollection(obj, List.class, List.class, Map.class, String.class, GenericRecord.class);
Note that this implementation doesn't support nested types in the Map. Hence, the type of key and value should be a class and not a collection. But it shouldn't be hard to add it.
public static boolean isInstanceOfGenericCollection(Object object, Class<?>... classes) {
if (classes.length == 0) return false;
if (classes.length == 1) return classes[0].isInstance(object);
if (classes[0].equals(List.class))
return object instanceof List && ((List<?>) object).stream().allMatch(item -> isInstanceOfGenericCollection(item, Arrays.copyOfRange(classes, 1, classes.length)));
if (classes[0].equals(Set.class))
return object instanceof Set && ((Set<?>) object).stream().allMatch(item -> isInstanceOfGenericCollection(item, Arrays.copyOfRange(classes, 1, classes.length)));
if (classes[0].equals(Map.class))
return object instanceof Map &&
((Map<?, ?>) object).keySet().stream().allMatch(classes[classes.length - 2]::isInstance) &&
((Map<?, ?>) object).values().stream().allMatch(classes[classes.length - 1]::isInstance);
return false;
}