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?
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?
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;
}
}
if(!myList.isEmpty() && myList.get(0) instanceof MyType){
// MyType object
}
value
qui 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.
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
Cela peut être utilisé si vous voulez vérifier que object
c'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.
}
}
if (list instanceof List && ((List) list).stream()
.noneMatch((o -> !(o instanceof MyType)))) {}
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());
setOfIntegers
et setOfStrings
?
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 );
}
}
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);
}
}
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
ou Set
, 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é comme
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);
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;
}