I want to implement the following function:
public boolean checkType(Vector> vec)
{
// return true if its Vector and false otherwise
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 Vector
s 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
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
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!!
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
}
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.