como instanciar List <MyType>?

Respostas:

49

Isso não é possível porque o tipo de dados é apagado em tempo de compilação dos genéricos. A única maneira possível de fazer isso é escrever algum tipo de wrapper que contenha o tipo que a lista contém:

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;
     }
}
Martijn Courteaux
fonte
Thanks, I think I will just pass the generic type into the function I'm calling to check and check both items.
Rocky Pulley
Can you elaborate with example
Thirumal
31
if(!myList.isEmpty() && myList.get(0) instanceof MyType){
    // MyType object
}
Sathish
fonte
6
...and for a empty list? Reflections?
Gewure
Yeap. That's the only option available for empty list. stackoverflow.com/questions/1942644/…
Sathish
11
This answer is not safe, because even if the 0 element is a MyType, the other elements could be other types. For example, maybe the list was declared as ArrayList<Object>, then a MyType was added, and then a String was added.
Adam Gawne-Cain
@AdamGawne-Cain It's not safe, but unfortunatelly the only solution for people NOT knowing much about the list. For example - I have a local variable value which returns Object, and I need to check - if it's a list, if it is, check if the list type instanceof my interface. No wrapper or parametrized type is useful here.
SocketByte
Where is myList declared?
IgorGanapolsky
9

You probably need to use reflection to get the types of them to check. To get the type of the List: Get generic type of java.util.List

evanwong
fonte
2
As far as I know, that only works for fields, but +1 for mentioning it.
Tim Pote
6

This could be used if you want to check that object is instance of List<T>, which is not empty:

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.
    }
}
Ivo Stoyanov
fonte
2
    if (list instanceof List && ((List) list).stream()
                                             .noneMatch((o -> !(o instanceof MyType)))) {}
dlaagniechy
fonte
1

If you are verifying if a reference of a List or Map value of Object is an instance of a Collection, just create an instance of required List and get its class...

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());
Marcello de Sales
fonte
What is the point of your setOfIntegers and setOfStrings?
DanielM
@DanielM just updated the sample. It must be using those references! Thanks!
Marcello de Sales
1

If this can't be wrapped with generics (@Martijn's answer) it's better to pass it without casting to avoid redundant list iteration (checking the first element's type guarantees nothing). We can cast each element in the piece of code where we iterate the 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 );
    }
}
kinjelom
fonte
0

You can use a fake factory to include many methods instead of using 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);
    }
}
LeLe
fonte
0

The major concern here is that the collections don't keep the type in the definition. The types are only available in runtime. I came up with a function to test complex collections (it has one constraint though).

Check if the object is an instance of a generic collection. In order to represent a collection,

  • No classes, always false
  • One class, it is not a collection and returns the result of instanceof evaluation
  • To represent a List or Set, the type of the list comes next e.g. {List, Integer} for List<Integer>
  • To represent a Map, the key and value types come next e.g. {Map, String, Integer} for Map<String, Integer>

More complex use cases could be generated using the same rules. For example in order to represent List<Map<String, GenericRecord>>, it can be called as

    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);

Note that this implementation doesn't support nested types in the Map. Hence, the type of key and value should be a class and not a collection. But it shouldn't be hard to add it.

    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;
    }
Iraj Hedayati
fonte