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:
Ta odpowiedź nie jest bezpieczna, ponieważ nawet jeśli element 0 jest MyType, pozostałe elementy mogą być innego typu. Na przykład, być może lista została zadeklarowana jako ArrayList <Object>, następnie dodano MyType, a następnie dodano String.
Adam Gawne-Cain,
@ AdamGawne-Cain To nie jest bezpieczne, ale niestety jedyne rozwiązanie dla osób NIE wiedzących zbyt wiele o liście. Na przykład - mam zmienną lokalną, 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.
O ile wiem, działa to tylko w przypadku pól, ale +1 za wzmiankę o tym.
Tim Pote,
6
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.
}
}
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());
Jaki jest sens twojego setOfIntegersi setOfStrings?
DanielM,
@DanielM właśnie zaktualizował przykład. Musi używać tych odniesień! Dzięki!
Marcello de Sales,
1
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 {
thrownew RuntimeException("Wrong class ("+item .getClass()+") of "+item );
}
}
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ę,
Żadnych zajęć, zawsze false
Jedna klasa nie jest zbiorem i zwraca wynik instanceofoceny
Aby przedstawić Listlub Set, typ listy jest następny, np. {List, Integer} dlaList<Integer>
Aby przedstawić a 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
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ć.
publicstaticbooleanisInstanceOfGenericCollection(Object object, Class<?>... classes){
if (classes.length == 0) returnfalse;
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);
returnfalse;
}
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; } }
źródło
if(!myList.isEmpty() && myList.get(0) instanceof MyType){ // MyType object }
źródło
value
która zwracaObject
i 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
źródło
Można tego użyć, jeśli chcesz sprawdzić, czy
object
jest to wystąpienieList<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. } }
źródło
if (list instanceof List && ((List) list).stream() .noneMatch((o -> !(o instanceof MyType)))) {}
źródło
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());
źródło
setOfIntegers
isetOfStrings
?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 ); } }
źródło
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); } }
źródło
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
instanceof
ocenyList
lubSet
, 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ć jakoMap<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; }
źródło