I came across the following code, a simple example of adding elements to List
List list = new ArrayList<Integer>();
ListIterator<Integer> litr = null;
list.add("A");
list.add("1");
list.add(5);
litr = list.listIterator();
while(litr.hasNext()){
System.out.println("UIterating " + litr.next());
}
I expected it to throw an ClassCastException
, but rather it wrote this to the console
A
1
5
which looks weird. When i tried:
List<Integer> list = new ArrayList<Integer>();
I got a compile time error.
I would be grateful if someone could explain how the String
objects are added to the ArrayList
Yeah, you can create an ArrayList<Object> but having said that, my advice for you is this: don't. Don't create Lists with mixed types since this suggests that your program design is broken and needs to be improved so that this sort of monster isn't needed.
Java ArrayList. Java ArrayList class uses a dynamic array for storing the elements. It is like an array, but there is no size limit. We can add or remove elements anytime.
ArrayList cannot hold primitive data types such as int, double, char, and long. With the introduction to wrapped class in java that was created to hold primitive data values. Objects of these types hold one value of their corresponding primitive type(int, double, short, byte).
You assigned the new ArrayList to an untyped List. Generic type restrictions don't apply to an untyped List, it will let you put whatever you want in it. The compiler does not keep track that your untyped List refers to something that was declared with a generic type.
In any case this wouldn't produce a ClassCastException, generics only affect compilation. At runtime
The case where you put the type on the list variable:
List<Integer> list = new ArrayList<Integer>();
is preferred, it should generate a compiler error telling you you're putting the wrong type in the collection.
There's a description of how legacy, non-generic code and generic code interoperate in this article:
In proper generic code, Collection would always be accompanied by a type parameter. When a generic type like Collection is used without a type parameter, it's called a raw type.
Most people's first instinct is that Collection really means
Collection<Object>
. However, as we saw earlier, it isn't safe to pass aCollection<Part>
in a place where aCollection<Object>
is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just likeCollection<?>
.But wait, that can't be right either! Consider the call to
getParts()
, which returns a Collection. This is then assigned to k, which is aCollection<Part>
. If the result of the call is aCollection<?>
, the assignment would be an error.In reality, the assignment is legal, but it generates an unchecked warning. The warning is needed, because the fact is that the compiler can't guarantee its correctness. We have no way of checking the legacy code in
getAssembly()
to ensure that indeed the collection being returned is a collection of Parts. The type used in the code is Collection, and one could legally insert all kinds of objects into such a collection.So, shouldn't this be an error? Theoretically speaking, yes; but practically speaking, if generic code is going to call legacy code, this has to be allowed. It's up to you, the programmer, to satisfy yourself that in this case, the assignment is safe because the contract of
getAssembly()
says it returns a collection of Parts, even though the type signature doesn't show this.
This is possible because of how generics are implemented in Java - using type erasure, and because Java supports raw types for backward compatibility with old versions of Java (1.4 and older).
Generics only exist in your source code. The compiler uses them to check the types at compile-time, but then throws away the generics. At runtime, a List<Integer>
is just a List
of objects, and it doesn't know that it's a list that should contain only Integer
objects.
Java supports the use of raw types such as List
instead of List<Integer>
for backward compatibility with old versions. When you use a raw type, as you are doing in your code above, you get a compiler warning. You should not use raw types in new code - only ever use them when you need to deal with old code that you can't change.
The combination of raw types and type erasure allows you to put types of objects in lists that you shouldn't be putting in there.
Because the List
at runtime doesn't know anything about the type that its elements are supposed to have, it doesn't check anything so you will not get a ClassCastException
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With