Add Y to Collection<X>, and Y not instanceof X. Why is no runtime exception thrown?

Viewed 82

The following method add anything to any collection. Why is no runtime exception thrown?

public static <T> void addAnything(Collection<? super T> c, Object t) {
    c.add((T) t);
}

For example,

List<String> list = new ArrayList<>();
addAnything(list, "ok");
addAnything(list, 2);
addAnything(list, true);
addAnything(list, new Object())
addAnything(list, new Anything())

Why is there not any exception thrown in this piece of code?

4 Answers

Two reasons: backwards compatibility and performance.

For backwards compatibility with old, unsafe code, generics in Java is implemented with "type erasure." The type system is enforced at compile time only. When you use a type cast you're telling the compiler "this is fine I know what I'm doing" and you merely get a warning.

Checking the types of the objects when they are added at run time would be a performance regression. Currently, ArrayList.add does not need to know anything about the object being added: it only works with the object reference. This means nothing about the object itself needs to be loaded from main memory into CPU registers or cache. If the type of the object had to be checked, the number of cache misses would increase dramatically, which slows the program down.

If you want a list that makes sure only Strings are added, you can use the Collections.checkedList method:

List<String> list = Collections.checkedList(new ArrayList<>(), String.class);
addAnything(list, 2); // run time error

Because the method as written is designed to add an instance of type Object to a list of anything that's a supertype of T (and as you surely know, for any T, Object is a supertype of T). So the method is treating your List<String> as a List<Object> and adding instances of Object to it. Which obviously is valid behaviour. You'll see the exceptions if you added non-String objects to the list and try to do something with its contents though.

There is no exception thrown ay runtime because of type erasure. Basically your ArrayList<String> is compiled just like a non-generic ArrayList, which means you can add any objects to it.

„Why is there no ClassCastException in the adding process?…Why is there not any exception thrown in this piece of code?…“

Because generics are checked at compile-time.

Your code is like the code in this classic example. And like it says there: „Although the code is compiled without error, it throws a runtime exception (java.lang.ClassCastException)…“

Related