Come posso far funzionare questo genere di cose? Posso controllare se (obj instanceof List<?>)
ma non se (obj instanceof List<MyType>)
. C'è un modo per farlo?
Come posso far funzionare questo genere di cose? Posso controllare se (obj instanceof List<?>)
ma non se (obj instanceof List<MyType>)
. C'è un modo per farlo?
Risposte:
Ciò non è possibile perché la cancellazione del tipo di dati in fase di compilazione dei generici. L'unico modo possibile per farlo è scrivere una sorta di wrapper che contenga il tipo contenuto nell'elenco:
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
che restituisce Object
e devo controllare: se è un elenco, se lo è, controlla se il tipo di elenco istanza della mia interfaccia. Nessun tipo di wrapper o parametrizzato è utile qui.
Probabilmente è necessario utilizzare la riflessione per ottenere i tipi da controllare. Per ottenere il tipo di List: Ottieni il tipo generico di java.util.List
Questo potrebbe essere usato se vuoi controllare che object
sia un'istanza di List<T>
, che non è vuota:
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)))) {}
Se stai verificando se un riferimento di un valore List o Map di Object è un'istanza di una Collection, crea semplicemente un'istanza di List richiesto e ottieni la sua classe ...
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
e setOfStrings
?
Se questo non può essere avvolto con generics (risposta di @ Martijn) è meglio passarlo senza casting per evitare iterazioni ridondanti dell'elenco (controllare il tipo del primo elemento non garantisce nulla). Possiamo eseguire il cast di ogni elemento nel pezzo di codice in cui iteriamo l'elenco.
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 );
}
}
Puoi usare una fake factory per includere molti metodi invece di usare 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);
}
}
La preoccupazione principale qui è che le raccolte non mantengono il tipo nella definizione. I tipi sono disponibili solo in runtime. Ho escogitato una funzione per testare raccolte complesse (tuttavia ha un vincolo).
Controlla se l'oggetto è un'istanza di una raccolta generica. Per rappresentare una collezione,
false
instanceof
valutazioneList
o Set
, il tipo di elenco viene dopo, ad esempio {List, Integer} perList<Integer>
Map
, seguono i tipi di chiave e valore, ad esempio {Map, String, Integer} forMap<String, Integer>
Casi d'uso più complessi potrebbero essere generati utilizzando le stesse regole. Ad esempio, per rappresentare List<Map<String, GenericRecord>>
, può essere chiamato come
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);
Tieni presente che questa implementazione non supporta i tipi nidificati nella mappa. Quindi, il tipo di chiave e valore dovrebbe essere una classe e non una raccolta. Ma non dovrebbe essere difficile aggiungerlo.
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;
}