Jak mogę sprawić, żeby coś takiego działało? Mogę sprawdzić, (obj instanceof List<?>)ale nie (obj instanceof List<MyType>). Czy można to zrobić?
Jak mogę sprawić, żeby coś takiego działało? Mogę sprawdzić, (obj instanceof List<?>)ale nie (obj instanceof List<MyType>). Czy można to zrobić?
Odpowiedzi:
Nie jest to możliwe, ponieważ wymazywanie typu danych w czasie kompilacji typów generycznych. Jedynym możliwym sposobem na zrobienie tego jest napisanie pewnego rodzaju opakowania, które przechowuje typ przechowywanej listy:
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
}
valuektóra zwraca Objecti muszę sprawdzić - jeśli jest to lista, jeśli jest, sprawdź, czy typ listy jest instancją mojego interfejsu. Żaden rodzaj opakowania lub sparametryzowany nie jest tutaj przydatny.
Prawdopodobnie będziesz musiał użyć refleksji, aby sprawdzić ich typy. Aby uzyskać typ listy: Pobierz ogólny typ java.util.List
Można tego użyć, jeśli chcesz sprawdzić, czy objectjest to wystąpienie List<T>, które nie jest puste:
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)))) {}
Jeśli sprawdzasz, czy odwołanie do wartości listy lub mapy obiektu jest instancją kolekcji, po prostu utwórz instancję wymaganej listy i pobierz jej klasę ...
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());
setOfIntegersi setOfStrings?
Jeśli nie można tego opakować generycznymi (odpowiedź @ Martijn), lepiej przekazać to bez rzutowania, aby uniknąć powtarzających się iteracji listy (sprawdzenie typu pierwszego elementu nic nie gwarantuje). Możemy rzutować każdy element z fragmentu kodu, w którym iterujemy 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 );
}
}
Możesz użyć fałszywej fabryki, aby dołączyć wiele metod zamiast używać 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);
}
}
Głównym problemem jest to, że kolekcje nie zachowują typu w definicji. Typy są dostępne tylko w czasie wykonywania. Wymyśliłem funkcję do testowania złożonych kolekcji (ma ona jednak jedno ograniczenie).
Sprawdź, czy obiekt jest instancją kolekcji ogólnej. Aby reprezentować kolekcję,
false instanceofocenyListlub Set, typ listy jest następny, np. {List, Integer} dlaList<Integer>Map, klucz i typy wartości są następne, np. {Map, String, Integer} forMap<String, Integer>Bardziej złożone przypadki użycia można generować przy użyciu tych samych reguł. Na przykład, aby przedstawić List<Map<String, GenericRecord>>, można go nazwać jako
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);
Należy zauważyć, że ta implementacja nie obsługuje typów zagnieżdżonych w Map. Stąd typ klucza i wartości powinien być klasą, a nie kolekcją. Ale nie powinno być trudno go dodać.
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;
}