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
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 a Collection<Part> in a place where a Collection<Object> is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection<?>.
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 a Collection<Part>. If the result of the call is a Collection<?>, 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.
Related
recently I read a piece of code which seems weird to me. As we know, we need to initialize the generic type in collections when we need to use them. Also, we know Collections can contain Collections as their elements.
The code:
public class Solution {
public static void main(String args[]) {
ArrayList res = returnlist();
System.out.print(res.get(0));
}
public static ArrayList<ArrayList<Integer>> returnlist() {
ArrayList result = new ArrayList();
ArrayList<Integer> content = new ArrayList<Integer>();
content.add(1);
result.add(content);
return result;
}}
My question is
why can we use ArrayList result = new ArrayList(); to create an object, since we have not gave the collection the actual type of element.
why can we use result.add(content); to add a collection to a collection with collection "result" is just a plain collection. We have not defined it as a ArrayList of ArrayList
Java generic collections are not stored with a type to ensure backwards compatibility with pre J2SE 5.0. Type information is removed when added to a generic collection. This is called Type Erasure.
This means that a generic collection can be assigned to a non generic reference and objects in a generic typed collection can be placed in an non generic, nontyped collection.
All Java generics really does is make sure you can't add the wrong type to a generic list and saves you from doing an explicit cast on retrieval; even though it is still done implicitly.
Further to this
the Java section of this answer goes a little deeper into what I just said
this article also covers basically what you were asking in a more complete way
other things to watch out for with Type Erasure
Just adding up to provide summarized answer
Old way :
(A) ArrayList result = new ArrayList();
will create an Arraylist to hold "Object"
New Way :
ArrayList<Integer> content = new ArrayList<Integer>();
this represents an Arraylist which will hold "Integer" objects. This was introduced for compile-time type check purposes.
why ?
Consider the first case. Its input type is Object. We know that Object is the super class of all classes. We can pass in an Integer object, String object and so on. When fetching the data the developer has to perform proper type casting. Say if the developer initially thinks the code will accept Integer objects so he adds the following typecast while fetching the data
Integer integer=(Integer) content.get(0);
This is supposed to work. But if mistakenly he passes a String it will result in run-time error.
How it can be avoided ?
By introducing compile time checks
How it works ?
when we specify parameterized type only Integer objects can be added to the ArrayList collection. Else it will show error.
content.add(3); // will work
content.add("HARSHAD"); // error shown
If parameterized generic types are for type checking purposes how correct data can be retrieved from the list ?
The compiler implicitly performs type conversion. See the sample code
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
Integer integer=list.get(0);
System.out.println(integer);
What the compiler actually does when you perform compilation ?
//perform type erasure
(B) List list=new ArrayList();
list.add(1);
list.add(2);
// the arraylist inturn accepts Object when you run the code
//add casting
Integer integer=(Integer)list.get(0);
Conclusion
If you see the codes (A) and (B) both are the same. Only difference is that in the second case the compiler implicitly does the same operation.
Finally to answer your question ...
ArrayList result = new ArrayList();
is allowed for backward compatibility purposes. Although this is not recommended.
Official link from Oracle docs explaining the same concept.
Generics were added to Java only in Java 5. Before that, when you use a collection, it always meant collection of objects. The old syntax is left as is for backward compatibility. So ArrayList result = new ArrayList() is actually creating an ArrayList<Object>. Since ArrayList is also an object, you can add content to the variable result.
why can we use ArrayList result = new ArrayList(); to create an object, since we have not give the collection the actual type of element.
Because java wants to it backward compatible. Generics is more of compiler feature for ensure type safety, collections can store any type of object at runtime.
Java compiler will not give you compiler error for this but it must have given you compiler warning that it is unsafe to use generic classes without type.
It may be a remnant from before generics came along to java (Java 4 or 5 I think).
Can someone please explain what the difference between ArrayList<?>, ArrayList and ArrayList<Object> is, and when to use each? Are they all same or does each have some different meaning at the implementation level?
ArrayList<Object> is specifically a list of Objects whereas ArrayList<?> is a list whose concrete type we are unsure of (meaning we can't add anything to the list except null). You would use the latter when the list's type is irrelevant, e.g. when the operation you want to perform does not depend on the type of the list. For instance:
public static boolean isBigEnough(ArrayList<?> list) {
return list.size() > 42;
}
This is all covered in the generics tutorial (see the wildcards section).
Finally, ArrayList with no type parameter is the raw type: the only reason it's even allowed is for backwards compatibility with Java versions under 5, and you should refrain from using it whenever possible.
ArrayList<?> means "an ArrayList instance containing a type which is to be determined"
ArrayList is the class of an ArrayList
An ArrayList<Object> means an instance of ArrayList containing Object types.
This looks like it could be a good write-up on this (and more): http://docs.oracle.com/javase/tutorial/java/generics/types.html
With the fear of sounding stupid.
Recently, I started with java/android.
I am loving it, but for the love of all that is good, I have come across an operator i can't seem to understand.
The thing is, I do not even know the name of it, so googling for it has been close to impossible. I have not found anything about it, not because it is not there, but because I do not even know where to start.
The operator is <someObject>. The same operator used in List<object>.
I actually became fascinated with it when using the AsyncTask class in android where I had to do something like
MyClass extends AsyncTask<String[], Drawable[], Drawable[]>{
...
Any info on this will be greatly appreciated.
It's not an operator - it's how you specify a generic type parameter.
It's probably best to start off with the Generics part of the Java Tutorial, then Gilad Bracha's paper, then consume the Java Generics FAQ for anything else. (There are lots of knotty corners in Java generics.)
It is not an operator, it is how you declare a parameterized type.
Before Java 5, you coulnd't say that your List was a "List of something", just a List with no type. So when you took an object out of your list, you had to cast it back to a specific type :
List strings = new ArrayList();
strings.add("hello");
String s = (String) strings.get(0);
With Java 5, you can specify the type of the elements, using angular brackets :
List<String> strings = new ArrayList<String>();
strings.add("hello");
String s = strings.get(0);
Because you know the exact type of the elements now, you don't have to cast the objects you get from the list anymore. Plus, the compiler won't let you add incompatible objects in the list :
List<String> strings = new ArrayList<String>();
strings.add(42); // Compiler error : expected a String, got an int
It's to do with generic types and type safety.
In "old java" you just had a List and it contained Objects - not type safe.
List l = new List();
l.add(new Type1()); // valid
l.add(new SomeOtherType()); // valid
These days you say
List<Type1> l = new List<Type1>();
l.add(new Type1()); // valid
l.add(new SomeOtherType()); // invalid since it is a list of Type1
The items in the List can be Type1 or any of it's subclasses.
If I understand your question, then List<Object> myList = new List<Object> means myList can hold Objects or any of its subclasses. This means myList can hold any object as they're all subclasses of Object.
Prior to Java 1.5, Java's collection classes were "untyped" (technically they still are). They could only store items that were derived from java.lang.Object. This was a bit cumbersome as you were forced to cast Object to your assumed contained type. This lead to all kinds of issues and oddities here and there.
Sun (now Oracle), added a "trick" to the language that would allow developers to specify a "type" for a container or other object. As I said, this is a "trick", the compiler performs type safety checks but in reality there is no change to the signature of the object emitted. It cleans up the code and adds a small amount of type safety but in reality its nothing more than a parlor trick.
Look up the Generics documentation provided with the JDK and the tutorials available.
Actually, the question should be
Creating an array of generic anything.
Why can't the compiler take care of it?
The following would be flagged as an error - cannot create generic array.
List<MyDTO>[] dtoLists = {new ArrayList<MyDTO>(), anExistingDtoList};
To overcome that, I need to
List<MyDTO>[] dtoLists = (List<MyDTO>[])Array.newInstance(ArrayList.class, 2);
dtoLists[0] = new ArrayList<MyDTO>();
dtoLists[1] = anExistingDtoList;
So, why can't the compiler convert the first case into the second case?
I do realise that generics are compile-time determinate and not run-time determinate, while arrays are run-time determinate and therefore need a determinate type in order to create an array.
What are the technological/logical barriers compiler designers would encounter that would prevent them being able to implement this?
Is the issue purely philosophical, concerning language orthogonality? If so, how would such a behaviour violate language orthogonality?
Is it a question of complexity? Explain the complexity.
I am hoping answers to my question would give me better insight into java compiler behaviour when it concerns generics.
Side note:
c'mon stop being trigger happy. The answers Array of Generic List
do not answer my question. Why can't compilers spontaneously perform the conversion?
Actually Java does create generic array for varargs, so you can do
List<MyDTO>[] dtoLists = array(new ArrayList<MyDTO>(), anExistingDtoList);
#SafeVarargs
static <E> E[] array(E... array)
{
return array;
}
As to why is explicit generic array creation forbidden, it has something to do with type erasure. (The same concern exists in the above solution, but suppressed by #SafeVarargs) However it is debatable; there are different ways to handle the concern, a compiler warning is probably enough. But they chose to outright ban it, probably because arrays are no longer important anyway now that we have generic collections
I do know that, relative to the workarounds to this issue, Array.newInstance() is an expensive method to call. IIRC it uses a native method to instantiate the array, amidst the other reflection involved. I can't offer any statistics, but this seems like a good enough reason for such functionality not to be automatically substituted in by the compiler in order to allow generic array creation. Especially given the existence of ArrayList, etc. it just doesn't seem like a pressing issue.
Compilers can spontaneously perform the conversion, they are just specified not to because generic arrays can't behave like non-generic arrays.
See 10.5. Array Store Exception:
For an array whose type is A[], where A is a reference type, an assignment to a component of the array is checked at run time to ensure that the value being assigned is assignable to the component.
If the type of the value being assigned is not assignment-compatible with the component type, an ArrayStoreException is thrown.
If the component type of an array were not reifiable, the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden.
A List<MyDTO>[] would not throw if we put some other kind of List in it, so it doesn't behave as an array. Note the last sentence from the quote: "This is why an array creation expression with a non-reifiable element type is forbidden." This is the reason, it's specified to be so. (And, for the record, this reasoning has always existed, so it was present when the question was posted in 2011.)
We can still do this:
#SuppressWarnings({"unchecked","rawtypes"})
List<MyDTO>[] dtoLists = new List[] {
new ArrayList<MyDTO>(), anExistingDtoList
};
Or this:
#SuppressWarnings("unchecked")
List<MyDTO>[] dtoLists = (List<MyDTO>[]) new List<?>[] {
new ArrayList<MyDTO>(), anExistingDtoList
};
(Besides statically checking the argument types, the varargs thing is equivalent: it creates a List[] and suppresses warnings.)
Now, sure, the specification could be changed to something like "If the type of the value being assigned is not assignment-compatible with the raw type of the component type...", but what is the point? It would save a handful of characters in some unusual situations but otherwise suppress warnings for those who don't understand the implications.
Furthermore, what the tutorial and other typical explanations I've seen don't demonstrate is just how baked in to the type system covariant arrays are.
For example, given the following declaration:
// (declaring our own because Arrays.fill is defined as
// void fill(Object[], Object)
// so the next examples would more obviously pass)
static <T> void fill(T[] arr, T elem) {
Arrays.fill(arr, elem);
}
Did you know that this compiles?
// throws ArrayStoreException
fill(new String[1], new Integer(0));
And this compiles too:
// doesn't throw ArrayStoreException
fill(dtoLists, new ArrayList<Float>());
Before Java 8, we could make those calls to fill fail by giving it the following declaration:
static <T, U extends T> void fill(T[] arr, U elem) {...}
But that was only a problem with type inference, and now it works "correctly", blindly putting List<Float> in to a List<MyDTO>[].
This is called heap pollution. It can cause a ClassCastException to be thrown sometime later, likely somewhere completely unrelated to the actions that actually caused the problem. Heap pollution with a generic container like List requires more obvious unsafe actions, like using raw types, but here, we can cause heap pollution implicitly and without any warnings.
Generic arrays (and really, arrays in general) only give us static checking in the simplest of circumstances.
So it's evident that the language designers thought it was better to just not allow them, and programmers who understand the problems they present can suppress warnings and bypass the restriction.
How remove the:
Type safety: The expression of type
List[] needs unchecked conversion to conform to List<Object>[]
compiler warning in the following expression:
List<Object>[] arrayOfList = new List[10];
Afaik the only way is to use #SuppressWarnings("unchecked"). At least if you want to avoid the raw type warning that occurs in Matthew's answer.
But you should rethink your design. An Array of Lists? Is that really necessary? Why not a List of Lists? And if it gets too complicated (a List of List of Map of List to...), use custom data types. This really makes the code much more readable.
And as an unrelated side note: You should write List<Object>[] arrayOfList. The brackets are part of the type, not the variable.
You cannot do any variation of this without a compiler warning. Generics and arrays do not play nice. Though you can suppress it with
#SuppressWarnings("unchecked")
final List<Object> arrayOfList[] = new List[10];
List arrayOfList[] = new List[10];
or
List[] arrayOfList = new List[10];
You can't have generic arrays in Java, so there is no reason to have a reference to one. It would not be type-safe, hence the warning. Of course, you're using <Object>, which means your lists are intended to contain any object. But the inability to have generic arrays still applies.
Also, note that the second version above (with [] attached to the type) is usually considered better style in Java. They are semantically equivalent.
There are a number of ways:
You can add `#SuppressWarnings("unchecked") before the assignment to tell the compiler to ignore the warning
Use raw types List[] arrayOfList = new List[10]; notice that in Java you usually put the [] after the type not the variable when declaring it - though using raw types is discouraged by Sun since they might be removed in a future version.
Don't use arrays, it's usually a bad idea to mix collections with arrays: List<List<Object>> listOfList = new ArrayList<List<Object>>;
Unfortunately, due to the fact that generics are implemented in Java using type erasure, you cannot create an array of a type with type parameters:
// This will give you a compiler error
List<Object>[] arrayOfList = new ArrayList<Object>[10];
See this in Angelika Langer's Java Generics FAQ for a detailed explanation.
You could remove the generics and use raw types, as Matthew Flaschen shows, or use a collection class instead of an array:
List<List<Object>> data = new ArrayList<List<Object>>();