Comment puis-je faire fonctionner ce genre de chose? Je peux vérifier si (obj instanceof List<?>)mais pas si (obj instanceof List<MyType>). Y a-t-il un moyen d'y parvenir?
Cela n'est pas possible car l'effacement du type de données au moment de la compilation des génériques. La seule façon possible de faire cela est d'écrire une sorte de wrapper contenant le type de la liste:
Cette réponse n'est pas sûre, car même si l'élément 0 est un MyType, les autres éléments peuvent être d'autres types. Par exemple, peut-être que la liste a été déclarée comme ArrayList <Object>, puis un MyType a été ajouté, puis une chaîne a été ajoutée.
Adam Gawne-Cain
@ AdamGawne-Cain Ce n'est pas sûr, mais malheureusement la seule solution pour les gens qui ne connaissent pas grand chose de la liste. Par exemple - j'ai une variable locale valuequi renvoie Object, et je dois vérifier - si c'est une liste, si c'est le cas, vérifiez si l'instance de type de liste de mon interface. Aucun wrapper ou type paramétré n'est utile ici.
Autant que je sache, cela ne fonctionne que pour les champs, mais +1 pour le mentionner.
Tim Pote
6
Cela peut être utilisé si vous voulez vérifier que objectc'est une instance de List<T>, qui n'est pas vide:
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.
}
}
Si vous vérifiez si une référence d'une valeur List ou Map d'Object est une instance d'une Collection, créez simplement une instance de List requise et obtenez sa 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());
Quel est le point de votre setOfIntegerset setOfStrings?
DanielM
@DanielM vient de mettre à jour l'exemple. Il doit utiliser ces références! Merci!
Marcello de Sales
1
Si cela ne peut pas être encapsulé avec des génériques (réponse de @ Martijn), il est préférable de le passer sans transtypage pour éviter une itération de liste redondante (vérifier le type du premier élément ne garantit rien). Nous pouvons convertir chaque élément dans le morceau de code où nous itérons la liste.
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 );
}
}
Le problème majeur ici est que les collections ne conservent pas le type dans la définition. Les types ne sont disponibles qu'au runtime. J'ai proposé une fonction pour tester des collections complexes (elle a cependant une contrainte).
Vérifiez si l'objet est une instance d'une collection générique. Afin de représenter une collection,
Pas de cours, toujours false
Une classe, ce n'est pas une collection et renvoie le résultat de l' instanceofévaluation
Pour représenter un Listou Set, le type de la liste vient ensuite, par exemple {List, Integer} pourList<Integer>
Pour représenter a Map, les types de clé et de valeur viennent ensuite, par exemple {Map, String, Integer} pourMap<String, Integer>
Des cas d'utilisation plus complexes pourraient être générés en utilisant les mêmes règles. Par exemple, pour représenter List<Map<String, GenericRecord>>, il peut être appelé comme
Notez que cette implémentation ne prend pas en charge les types imbriqués dans la carte. Par conséquent, le type de clé et de valeur doit être une classe et non une collection. Mais il ne devrait pas être difficile de l'ajouter.
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;
}
Réponses:
Cela n'est pas possible car l'effacement du type de données au moment de la compilation des génériques. La seule façon possible de faire cela est d'écrire une sorte de wrapper contenant le type de la liste:
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; } }
la source
if(!myList.isEmpty() && myList.get(0) instanceof MyType){ // MyType object }
la source
value
qui renvoieObject
, et je dois vérifier - si c'est une liste, si c'est le cas, vérifiez si l'instance de type de liste de mon interface. Aucun wrapper ou type paramétré n'est utile ici.Vous devrez probablement utiliser la réflexion pour en vérifier les types. Pour obtenir le type de la liste: Obtenez le type générique de java.util.List
la source
Cela peut être utilisé si vous voulez vérifier que
object
c'est une instance deList<T>
, qui n'est pas vide: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. } }
la source
if (list instanceof List && ((List) list).stream() .noneMatch((o -> !(o instanceof MyType)))) {}
la source
Si vous vérifiez si une référence d'une valeur List ou Map d'Object est une instance d'une Collection, créez simplement une instance de List requise et obtenez sa 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());
la source
setOfIntegers
etsetOfStrings
?Si cela ne peut pas être encapsulé avec des génériques (réponse de @ Martijn), il est préférable de le passer sans transtypage pour éviter une itération de liste redondante (vérifier le type du premier élément ne garantit rien). Nous pouvons convertir chaque élément dans le morceau de code où nous itérons la liste.
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 ); } }
la source
Vous pouvez utiliser une fausse usine pour inclure de nombreuses méthodes au lieu d'utiliser 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 source
Le problème majeur ici est que les collections ne conservent pas le type dans la définition. Les types ne sont disponibles qu'au runtime. J'ai proposé une fonction pour tester des collections complexes (elle a cependant une contrainte).
Vérifiez si l'objet est une instance d'une collection générique. Afin de représenter une collection,
false
instanceof
évaluationList
ouSet
, le type de la liste vient ensuite, par exemple {List, Integer} pourList<Integer>
Map
, les types de clé et de valeur viennent ensuite, par exemple {Map, String, Integer} pourMap<String, Integer>
Des cas d'utilisation plus complexes pourraient être générés en utilisant les mêmes règles. Par exemple, pour représenter
List<Map<String, GenericRecord>>
, il peut être appelé commeMap<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);
Notez que cette implémentation ne prend pas en charge les types imbriqués dans la carte. Par conséquent, le type de clé et de valeur doit être une classe et non une collection. Mais il ne devrait pas être difficile de l'ajouter.
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; }
la source