Checking Java Collection Generic Type for an Empty Collection

前端 未结 4 1640
情话喂你
情话喂你 2021-01-15 05:56

I want to implement the following function:

public boolean checkType(Vector vec)
{
  // return true if its Vector and false otherwise         


        
相关标签:
4条回答
  • 2021-01-15 06:36

    Generic type parameters are unrecoverable (except for some special cases) at runtime because of type erasure. This means that at runtime, both Vector<Integer> and Vector<String> are simply just Vectors and their elements are just Object references.

    Only the actual runtime classes (discoverable by instanceof checks as you pointed out) of the individual elements make the notion of the element type, otherwise the Vector itself has no idea what it's element type is.

    So basically, an empty vector of any type is equal to empty vector of any other type. It is even safe to cast it like this:

    Vector<String> noStrings = (Vector<String>) new Vector<Integers>();
    

    However there is one problem, because although you can say that empty vector conforms to any required element type, this statement stands only as long as the vector stays empty. Because if you do this:

    Vector<Integer> ints = new Vector<Integer>(); // empty
    Vector<String> strings = (Vector<String>) ints; // unchecked warning, but still possibly ok
    ints.add(1); // here comes trouble
    String s = strings.get(1); // oh oh, ClassCastException
    

    EDIT:

    To answer you second question: No it isn't possible to write anything like this:

    public <T> boolean checkType(Vector<T> vec) {
        return T instanceof Integer; // impossible
        return T == Integer; // impossible
        return T.class == Integer.class // impossible
        return vec instanceof (Vector<Integer>); // impossible
    }
    

    However you can write a method that uses a class token as an input parameter:

    static <T> boolean checkElementType(Collection<?> collection, Class<T> elementType) {
        for (Object object : collection) {
            if (!elementType.isAssignableFrom(object.getClass())) {
                return false;
            }
        }
        return true;
    }
    

    And then you can use it like this:

    List<?> list1 = Arrays.asList(1, 2, 3);
    List<?> list2 = Arrays.asList(1, 2, 3, "a");
    System.out.println(checkElementType(list1, Integer.class)); // true
    System.out.println(checkElementType(list2, Integer.class)); // false
    
    0 讨论(0)
  • 2021-01-15 06:41
    public boolean checkType(Vector<?> vec)
    {
      if(!vec.isEmpty())
      {
        if("String".equals(vec.get(0).getClass().getSimpleName()))
                 return false;
        else if("Integer".equals(vec.get(0).getClass().getSimpleName()))
                 return true;               
       }
    }
    

    Hope this helps!!

    0 讨论(0)
  • 2021-01-15 06:52

    Because of type erasure, this is not possible.

    Even if the vector is not empty, the collections library gives no real protection against putting things of different types in:

        Vector v = new Vector<Integer>();
        v.add("foo");
        System.out.println(v.get(0));
    

    will run without error and nicely print the string value that shouldn't have been allowed.

    Thus any attempt to determine the generic type at run-time, even by checking the type of the first element, is not reliable.

    What you can do in your own generic code is to add a constructor argument and a field holding the value, so that your code does know the type at runtime.

    This tactic would be difficult to apply to the entire collections library, but a start at it for Vector would be:

    public class TypedVector<E> extends Vector<E> {
    
        private Class<E> genericClass;
    
        public TypedVector(Class<E> clazz) {
            super();
            this.genericClass = clazz;
        }
    
        // many other constructors ... but probably not all of them
    
        public Class<?> getGenericClass() {
            return genericClass;
        }
    
        @Override
        public boolean add(E e) {
            if (!(genericClass.isAssignableFrom(e.getClass())))
                    throw new IllegalArgumentException("Incorrect class");
            return super.add(e);
        }
    
        // many other overrides for run-time type safety
    }
    
    0 讨论(0)
  • 2021-01-15 07:00

    Anyone who tells you that this is possible at all is wrong. At runtime, there is no way to tell whether an empty Vector was a Vector<Integer> or Vector<Rainbow>.

    That's more or less the definition of type erasure.

    0 讨论(0)
提交回复
热议问题