I just stumbled upon the compiler treating these two terms differently. when I type:
LinkedList<String> list = new LinkedList();
I get a compiler warning about a raw type. however:
LinkedList<String> list = new LinkedList<>();
removes the warning. It seems to me as though the two statements mean essentially the same thing (i.e. create a new LinkedList with no specified object type). Why then does the complier all ow the empty generics? What is the difference here?
The statements do not mean the same thing at all.
The first statement tries to fit an untyped LinkedList into a declared generic LinkedList<String> and appropriately throws a warning.
The second statement, valid in Java 1.7 onward, uses type inference to guess the type parameter by using the declaring type's type parameter. In addition, sometimes this can be used in method calls. It doesn't always work, however.
See this page for more info.
It's the diamond operator in Java 7, that helps you save writing the type again. In Java 7 this is equivalent to the same generic type argument that is used on the left side of the declaration. So the initialization is type safe and no warning is issued.
With LinkedList<>, you use the new Diamond Operator, from java 7.
The Diamod operator uses the generic value setted in the left side of the line.
In Java 6, this doesnt works!
The diamond operator, however, allows the right hand side of the
assignment to be defined as a true generic instance with the same type
parameters as the left side... without having to type those parameters
again. It allows you to keep the safety of generics with almost the
same effort as using the raw type.
I think the key thing to understand is that raw types (with no <>)
cannot be treated the same as generic types. When you declare a raw
type, you get none of the benefits and type checking of generics. You
also have to keep in mind that generics are a general purpose part of
the Java language... they don't just apply to the no-arg constructors
of Collections!
Extracted from: https://stackoverflow.com/a/10093701/1281306
Backword compatibility (Inter-operating with legacy code) is the reason why java allows above signature. Generics are compile time syntax only. At runtime "all generic" syntax will be removed. You will just see if you de-compile any class file. Read this documentation.
LinkedList list = new LinkedList();
Related
The diamond operator in java 7 allows code like the following:
List<String> list = new LinkedList<>();
However in Java 5/6, I can simply write:
List<String> list = new LinkedList();
My understanding of type erasure is that these are exactly the same. (The generic gets removed at runtime anyway).
Why bother with the diamond at all? What new functionality / type safety does it allow? If it doesn't yield any new functionality why do they mention it as a feature? Is my understanding of this concept flawed?
The issue with
List<String> list = new LinkedList();
is that on the left hand side, you are using the generic type List<String> where on the right side you are using the raw type LinkedList. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unless
you absolutely have to.
Now, if Java had generics from the beginning and didn't have types, such as LinkedList, that were originally created before it had generics, it probably could have made it so that the constructor for a generic type automatically infers its type parameters from the left-hand side of the assignment if possible. But it didn't, and it must treat raw types and generic types differently for backwards compatibility. That leaves them needing to make a slightly different, but equally convenient, way of declaring a new instance of a generic object without having to repeat its type parameters... the diamond operator.
As far as your original example of List<String> list = new LinkedList(), the compiler generates a warning for that assignment because it must. Consider this:
List<String> strings = ... // some list that contains some strings
// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);
Generics exist to provide compile-time protection against doing the wrong thing. In the above example, using the raw type means you don't get this protection and will get an error at runtime. This is why you should not use raw types.
// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);
The diamond operator, however, allows the right hand side of the assignment to be defined as a true generic instance with the same type parameters as the left side... without having to type those parameters again. It allows you to keep the safety of generics with almost the same effort as using the raw type.
I think the key thing to understand is that raw types (with no <>) cannot be treated the same as generic types. When you declare a raw type, you get none of the benefits and type checking of generics. You also have to keep in mind that generics are a general purpose part of the Java language... they don't just apply to the no-arg constructors of Collections!
Your understanding is slightly flawed. The diamond operator is a nice feature as you don't have to repeat yourself. It makes sense to define the type once when you declare the type but just doesn't make sense to define it again on the right side. The DRY principle.
Now to explain all the fuzz about defining types. You are right that the type is removed at runtime but once you want to retrieve something out of a List with type definition you get it back as the type you've defined when declaring the list otherwise it would lose all specific features and have only the Object features except when you'd cast the retrieved object to it's original type which can sometimes be very tricky and result in a ClassCastException.
Using List<String> list = new LinkedList() will get you rawtype warnings.
This line causes the [unchecked] warning:
List<String> list = new LinkedList();
So, the question transforms: why [unchecked] warning is not suppressed automatically only for the case when new collection is created?
I think, it would be much more difficult task then adding <> feature.
UPD: I also think that there would be a mess if it were legally to use raw types 'just for a few things'.
In theory, the diamond operator allows you to write more compact (and readable) code by saving repeated type arguments. In practice, it's just two confusing chars more giving you nothing. Why?
No sane programmer uses raw types in new code. So the compiler could simply assume that by writing no type arguments you want it to infer them.
The diamond operator provides no type information, it just says the compiler, "it'll be fine". So by omitting it you can do no harm. At any place where the diamond operator is legal it could be "inferred" by the compiler.
IMHO, having a clear and simple way to mark a source as Java 7 would be more useful than inventing such strange things. In so marked code raw types could be forbidden without losing anything.
Btw., I don't think that it should be done using a compile switch. The Java version of a program file is an attribute of the file, no option at all. Using something as trivial as
package 7 com.example;
could make it clear (you may prefer something more sophisticated including one or more fancy keywords). It would even allow to compile sources written for different Java versions together without any problems. It would allow introducing new keywords (e.g., "module") or dropping some obsolete features (multiple non-public non-nested classes in a single file or whatsoever) without losing any compatibility.
When you write List<String> list = new LinkedList();, compiler produces an "unchecked" warning. You may ignore it, but if you used to ignore these warnings you may also miss a warning that notifies you about a real type safety problem.
So, it's better to write a code that doesn't generate extra warnings, and diamond operator allows you to do it in convenient way without unnecessary repetition.
All said in the other responses are valid but the use cases are not completely valid IMHO. If one checks out Guava and especially the collections related stuff, the same has been done with static methods. E.g. Lists.newArrayList() which allows you to write
List<String> names = Lists.newArrayList();
or with static import
import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");
Guava has other very powerful features like this and I actually can't think of much uses for the <>.
It would have been more useful if they went for making the diamond operator behavior the default, that is, the type is inferenced from the left side of the expression or if the type of the left side was inferenced from the right side. The latter is what happens in Scala.
The point for diamond operator is simply to reduce typing of code when declaring generic types. It doesn't have any effect on runtime whatsoever.
The only difference if you specify in Java 5 and 6,
List<String> list = new ArrayList();
is that you have to specify #SuppressWarnings("unchecked") to the list (otherwise you will get an unchecked cast warning). My understanding is that diamond operator is trying to make development easier. It's got nothing to do on runtime execution of generics at all.
The diamond operator in java 7 allows code like the following:
List<String> list = new LinkedList<>();
However in Java 5/6, I can simply write:
List<String> list = new LinkedList();
My understanding of type erasure is that these are exactly the same. (The generic gets removed at runtime anyway).
Why bother with the diamond at all? What new functionality / type safety does it allow? If it doesn't yield any new functionality why do they mention it as a feature? Is my understanding of this concept flawed?
The issue with
List<String> list = new LinkedList();
is that on the left hand side, you are using the generic type List<String> where on the right side you are using the raw type LinkedList. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unless
you absolutely have to.
Now, if Java had generics from the beginning and didn't have types, such as LinkedList, that were originally created before it had generics, it probably could have made it so that the constructor for a generic type automatically infers its type parameters from the left-hand side of the assignment if possible. But it didn't, and it must treat raw types and generic types differently for backwards compatibility. That leaves them needing to make a slightly different, but equally convenient, way of declaring a new instance of a generic object without having to repeat its type parameters... the diamond operator.
As far as your original example of List<String> list = new LinkedList(), the compiler generates a warning for that assignment because it must. Consider this:
List<String> strings = ... // some list that contains some strings
// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);
Generics exist to provide compile-time protection against doing the wrong thing. In the above example, using the raw type means you don't get this protection and will get an error at runtime. This is why you should not use raw types.
// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);
The diamond operator, however, allows the right hand side of the assignment to be defined as a true generic instance with the same type parameters as the left side... without having to type those parameters again. It allows you to keep the safety of generics with almost the same effort as using the raw type.
I think the key thing to understand is that raw types (with no <>) cannot be treated the same as generic types. When you declare a raw type, you get none of the benefits and type checking of generics. You also have to keep in mind that generics are a general purpose part of the Java language... they don't just apply to the no-arg constructors of Collections!
Your understanding is slightly flawed. The diamond operator is a nice feature as you don't have to repeat yourself. It makes sense to define the type once when you declare the type but just doesn't make sense to define it again on the right side. The DRY principle.
Now to explain all the fuzz about defining types. You are right that the type is removed at runtime but once you want to retrieve something out of a List with type definition you get it back as the type you've defined when declaring the list otherwise it would lose all specific features and have only the Object features except when you'd cast the retrieved object to it's original type which can sometimes be very tricky and result in a ClassCastException.
Using List<String> list = new LinkedList() will get you rawtype warnings.
This line causes the [unchecked] warning:
List<String> list = new LinkedList();
So, the question transforms: why [unchecked] warning is not suppressed automatically only for the case when new collection is created?
I think, it would be much more difficult task then adding <> feature.
UPD: I also think that there would be a mess if it were legally to use raw types 'just for a few things'.
In theory, the diamond operator allows you to write more compact (and readable) code by saving repeated type arguments. In practice, it's just two confusing chars more giving you nothing. Why?
No sane programmer uses raw types in new code. So the compiler could simply assume that by writing no type arguments you want it to infer them.
The diamond operator provides no type information, it just says the compiler, "it'll be fine". So by omitting it you can do no harm. At any place where the diamond operator is legal it could be "inferred" by the compiler.
IMHO, having a clear and simple way to mark a source as Java 7 would be more useful than inventing such strange things. In so marked code raw types could be forbidden without losing anything.
Btw., I don't think that it should be done using a compile switch. The Java version of a program file is an attribute of the file, no option at all. Using something as trivial as
package 7 com.example;
could make it clear (you may prefer something more sophisticated including one or more fancy keywords). It would even allow to compile sources written for different Java versions together without any problems. It would allow introducing new keywords (e.g., "module") or dropping some obsolete features (multiple non-public non-nested classes in a single file or whatsoever) without losing any compatibility.
When you write List<String> list = new LinkedList();, compiler produces an "unchecked" warning. You may ignore it, but if you used to ignore these warnings you may also miss a warning that notifies you about a real type safety problem.
So, it's better to write a code that doesn't generate extra warnings, and diamond operator allows you to do it in convenient way without unnecessary repetition.
All said in the other responses are valid but the use cases are not completely valid IMHO. If one checks out Guava and especially the collections related stuff, the same has been done with static methods. E.g. Lists.newArrayList() which allows you to write
List<String> names = Lists.newArrayList();
or with static import
import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");
Guava has other very powerful features like this and I actually can't think of much uses for the <>.
It would have been more useful if they went for making the diamond operator behavior the default, that is, the type is inferenced from the left side of the expression or if the type of the left side was inferenced from the right side. The latter is what happens in Scala.
The point for diamond operator is simply to reduce typing of code when declaring generic types. It doesn't have any effect on runtime whatsoever.
The only difference if you specify in Java 5 and 6,
List<String> list = new ArrayList();
is that you have to specify #SuppressWarnings("unchecked") to the list (otherwise you will get an unchecked cast warning). My understanding is that diamond operator is trying to make development easier. It's got nothing to do on runtime execution of generics at all.
The diamond operator in java 7 allows code like the following:
List<String> list = new LinkedList<>();
However in Java 5/6, I can simply write:
List<String> list = new LinkedList();
My understanding of type erasure is that these are exactly the same. (The generic gets removed at runtime anyway).
Why bother with the diamond at all? What new functionality / type safety does it allow? If it doesn't yield any new functionality why do they mention it as a feature? Is my understanding of this concept flawed?
The issue with
List<String> list = new LinkedList();
is that on the left hand side, you are using the generic type List<String> where on the right side you are using the raw type LinkedList. Raw types in Java effectively only exist for compatibility with pre-generics code and should never be used in new code unless
you absolutely have to.
Now, if Java had generics from the beginning and didn't have types, such as LinkedList, that were originally created before it had generics, it probably could have made it so that the constructor for a generic type automatically infers its type parameters from the left-hand side of the assignment if possible. But it didn't, and it must treat raw types and generic types differently for backwards compatibility. That leaves them needing to make a slightly different, but equally convenient, way of declaring a new instance of a generic object without having to repeat its type parameters... the diamond operator.
As far as your original example of List<String> list = new LinkedList(), the compiler generates a warning for that assignment because it must. Consider this:
List<String> strings = ... // some list that contains some strings
// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);
Generics exist to provide compile-time protection against doing the wrong thing. In the above example, using the raw type means you don't get this protection and will get an error at runtime. This is why you should not use raw types.
// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);
The diamond operator, however, allows the right hand side of the assignment to be defined as a true generic instance with the same type parameters as the left side... without having to type those parameters again. It allows you to keep the safety of generics with almost the same effort as using the raw type.
I think the key thing to understand is that raw types (with no <>) cannot be treated the same as generic types. When you declare a raw type, you get none of the benefits and type checking of generics. You also have to keep in mind that generics are a general purpose part of the Java language... they don't just apply to the no-arg constructors of Collections!
Your understanding is slightly flawed. The diamond operator is a nice feature as you don't have to repeat yourself. It makes sense to define the type once when you declare the type but just doesn't make sense to define it again on the right side. The DRY principle.
Now to explain all the fuzz about defining types. You are right that the type is removed at runtime but once you want to retrieve something out of a List with type definition you get it back as the type you've defined when declaring the list otherwise it would lose all specific features and have only the Object features except when you'd cast the retrieved object to it's original type which can sometimes be very tricky and result in a ClassCastException.
Using List<String> list = new LinkedList() will get you rawtype warnings.
This line causes the [unchecked] warning:
List<String> list = new LinkedList();
So, the question transforms: why [unchecked] warning is not suppressed automatically only for the case when new collection is created?
I think, it would be much more difficult task then adding <> feature.
UPD: I also think that there would be a mess if it were legally to use raw types 'just for a few things'.
In theory, the diamond operator allows you to write more compact (and readable) code by saving repeated type arguments. In practice, it's just two confusing chars more giving you nothing. Why?
No sane programmer uses raw types in new code. So the compiler could simply assume that by writing no type arguments you want it to infer them.
The diamond operator provides no type information, it just says the compiler, "it'll be fine". So by omitting it you can do no harm. At any place where the diamond operator is legal it could be "inferred" by the compiler.
IMHO, having a clear and simple way to mark a source as Java 7 would be more useful than inventing such strange things. In so marked code raw types could be forbidden without losing anything.
Btw., I don't think that it should be done using a compile switch. The Java version of a program file is an attribute of the file, no option at all. Using something as trivial as
package 7 com.example;
could make it clear (you may prefer something more sophisticated including one or more fancy keywords). It would even allow to compile sources written for different Java versions together without any problems. It would allow introducing new keywords (e.g., "module") or dropping some obsolete features (multiple non-public non-nested classes in a single file or whatsoever) without losing any compatibility.
When you write List<String> list = new LinkedList();, compiler produces an "unchecked" warning. You may ignore it, but if you used to ignore these warnings you may also miss a warning that notifies you about a real type safety problem.
So, it's better to write a code that doesn't generate extra warnings, and diamond operator allows you to do it in convenient way without unnecessary repetition.
All said in the other responses are valid but the use cases are not completely valid IMHO. If one checks out Guava and especially the collections related stuff, the same has been done with static methods. E.g. Lists.newArrayList() which allows you to write
List<String> names = Lists.newArrayList();
or with static import
import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");
Guava has other very powerful features like this and I actually can't think of much uses for the <>.
It would have been more useful if they went for making the diamond operator behavior the default, that is, the type is inferenced from the left side of the expression or if the type of the left side was inferenced from the right side. The latter is what happens in Scala.
The point for diamond operator is simply to reduce typing of code when declaring generic types. It doesn't have any effect on runtime whatsoever.
The only difference if you specify in Java 5 and 6,
List<String> list = new ArrayList();
is that you have to specify #SuppressWarnings("unchecked") to the list (otherwise you will get an unchecked cast warning). My understanding is that diamond operator is trying to make development easier. It's got nothing to do on runtime execution of generics at all.
I have the next code:
ArrayList value = new ArrayList<Integer>(); // 1
value.add("Test"); // 2
I'm trying to understand line 2. Although I can see that value.add("Test"); compiles without errors, I can't see the reason it doesn't throw a runtime exception. If value is referencing a generic ArrayList object, why Java allows to add a String to it? Can anyone explain it to me?
The closest explanation I've found about this is described here, but I still don't understand the core reason:
Stack s = new Stack<Integer>()
This is a legal conversion from a parameterized type to a raw type. You will be able to push value of any type. However, any such operation will result in an "unchecked call" warning.
Generic types are erased during compilation. So at runtime, an ArrayList is a raw ArrayList, no matter if you defined it as generic or not.
In your case, the code compiles as your ArrayList declaration is not generic, and it runs fine because of type erasure.
ArrayList value this is your type declaration which is not generic. That is why compiler allows you to add any Object to the list.
I've a doubt reading this written in the Java tutorial:
In the introduction, we saw invocations of the generic type
declaration List, such as List. In the invocation (usually
called a parameterized type), all occurrences of the formal type
parameter (E in this case) are replaced by the actual type argument
(in this case, Integer).
but if there are no restrictions the formal type parameter is not replaced by Object?
Why is said that E is replaced by Integer?
Also, here, in the Java tutorial is said:
To reference the generic Box class from within your code, you must
perform a generic type invocation, which replaces T with some concrete
value, such as Integer:
but, again, thanks to the erasure a compile time T in box class is replaced
by Object and not by Integer. Integer type is written only for casting operations.
In fact, still in the same tutorial is said:
During the type erasure process, the Java compiler erases all type
parameters and replaces each with its first bound if the type
parameter is bounded, or Object if the type parameter is unbounded.
I'm really confused. Which is the truth?
Is T replaced by Integer or by Object?
You speak of different things.
The citations from the tutorial speak about type instantiation. This has nothing to do with type erasure, which is a IMHO misnamed concept, and simply means that the generic types are not available at runtime anymore.
But at compile time they are, and instantiation happens at compile time.
To answer your question, "at compile time" is a broad thing. THe following hapens all at compile time:
read source files
lexical analysis
parsing
...
type checking
...
code generation
The list is, by no means, complete, mind you.
However, as you see, during type checking, the compiler knows your type instantiations and can check them.
Later, it emits byte code, and since byte code has no way of representing generics, the types are "erased", which means, a cast is inserted here and there.
So, your assumption that "compile time" is somehow an instant where everything happens at once is not correct.
Further edit:
I think you take all this (i.e. the word "replace") too literally. For sure, the compiler has some data structures where the types and names and scopes of all items in the program are held.
Look, it's quite simple in principle, if we have:
static <X> List<X> meth(X[] arr) { .... }
And later, you do:
Integer arr = new Integer[100];
List<Integer> list = meth(arr);
Integer foo = list.get(1);
then you are instantiating the type of the meth method:
static List<Integer> meth(Integer[] arr) { .... }
The point of the generics is to say that meth works for any type. This is just what the compiler checks. And it will know, that, for all X if you pass an array of X, you get back a list of X, hence, since you passed Integer[], the result must be List<Integer> and the list assignment is correct. Furthermore, the compiler knows, that ** for all X **, if you get an element from a List<X>, it will be an X.
Therefore, the compiler notes and checks that foo is an Integer. Later, on code generation, it will insert there a cast to Integer, because, due to type erasure, the return value from List.get is Object.
Note also, that "replace" does not mean that the compiler somehow alters your code. It just creates (maybe temporary) from the generic type signature a non-generic one (by substituting - if you like this better - all the type parameters with their actual types), and uses this to check the type.
It is just like in math, if I say: Please replace the a with 42 and check if the equation is true:
a + 1 = 43
then it makes no sense to ask "where exactly" this replacement takes place. Most probably in your brain.
the formal type parameter is not replaced by Object?
Generic type represented as Object in runtime. But you can get information about <YourType> with reflection. Erasure relates to compatibility with old clases. It was a bad idea. Article about it.