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.
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.
I have worked with Generics in Java quite a bit and know about their type erasure capability and type safety.But there has been some criticism about their occurrence in JAVA.
My question is what are the best optimized scenarios to utilize Generics and I am not talking about that '< E > void printArray' stuff.Are there any advantageous usage scenarios or avoiding them will do.Also in cases I have saw that they don't provide complete type safety.
Now consider the code below:
// create a vector of strings
Vector<String> strings = new Vector<String>(10);
// cast it to a vector of objects
Vector<Object> objects = (Vector<Object>)strings;
// insert an object into the vector
objects.add(new Object());
Will the above code cause a runtime exception.No I test.It doesnt actually compile.
Now see the similar code using arrays:
// create an array of strings
String[] strings = new String[10];
// cast it to an array of objects
Object[] objects = strings;
// insert an object into the array
objects[0] = new Object();
Does it compile,yes despite the fact that casting a array of strings
to an array of objects introduces the possibility of run-time errors.
Line 8 demonstrates this, causing a run-time exception.Now the type erasure should work in first example and let it compile and then cause a runtime exception as it worked in arrays example,because cast worked for array example at compile time using array type String[] which is analogous to the vector type Vector<String> So Do generics really help you at compile time? I think this behaviour is forced upon developers,because the parameterised types do not exist at run-time, and hence the variable created in line 2 of fist example just has type Vector.
Also for what specifically helpful is Generalized target type inference in
java 8?
There is an interesting study of Python modules. It concluded that 97.4% of "variables" could be assigned "single static type using a conservative nominal type system using single inheritance". If we allow for parametric polymorphism in the type system (i.e. generics) this number increases by 0.5%.
This is one datapoint suggesting that the use of generic types in "real programs" is actually quite infrequent. Further anecdotal evidence may be found in many talks about the Go Programming Language, e.g. this one by Brad Fitzpatrick "No generics. It sucks sometimes, but rarely."
Generics are a nice feature that allows the type system to capture a slightly larger portion of your program. It is also nice from the UX perspective, because it saves typing (with your fingers) and IDEs can offer autocompletion (compare that to doing manual typecasts to Object and back in Java 1.4). But generics do not appear that often and can be quite easily replaced by code generation and therefore seem to me to be hardly essential in software development.
(In fact, Go is cheating here. They do have generic types, but only for the 5 or so built in datastructures (maps, channels, ...). Users cannot define their own generic types and they cannot use generic type wildcards when specifying function arguments. Apparently, this suffices to satisfy the author of the presentation.)
Due to type erasure the generic type information is lost during runtime.
Therefore the compiler makes sure that your first example does not compile otherwise it would cause a runtime exception.
Arrays are covariant, that makes this code compile
class A {}
class B extends A {}
class C extends A {}
A a = new B();
A[] as = new B[0];
as[0] = new C(); // runtime error, as is an array of Bs at runtime
But arrays in java have a reifiable type, that is for arrays the array type is known at runtime and therefore the assignment on the wrong element type causes an ArrayStoreException at runtime. tl;dr arrays are broken in java.
Read this for more information: https://dzone.com/articles/covariance-and-contravariance
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.
Bear with me for a while. I know this sounds subjective and argumentative for a while, but I swear there is a question mark at the end, and that the question can actually be answered in an objective way...
Coming from a .NET and C# background, I have during recent years been spoiled with the syntactic sugar that generics combined with extension methods provide in many .NET solutions to common problems. One of the key features that make C# generics so extremely powerful is the fact that if there is enough information elsewhere, the compiler can infer the type arguments, so I almost never have to write them out. You don't have to write many lines of code before you realize how many keystrokes you save on that. For example, I can write
var someStrings = new List<string>();
// fill the list with a couple of strings...
var asArray = someStrings.ToArray();
and C# will just know that I mean the first var to be List<string>, the second one to be string[] and that .ToArray() really is .ToArray<string>().
Then I come to Java.
I have understood enough about Java generics to know that they are fundamentally different, above else in the fact that the compiler doesn't actually compile to generic code - it strips the type arguments and makes it work anyway, in some (quite complicated) way (that I haven't really understood yet). But even though I know generics in Java is fundamentally different, I can't understand why constructs like these are necessary:
ArrayList<String> someStrings = new ArrayList<String>();
// fill the list with a couple of strings...
String[] asArray = someStrings.toArray(new String[0]); // <-- HERE!
Why on earth must I instantiate a new String[], with no elements in it, that won't be used for anything, for the Java compiler to know that it is String[] and not any other type of array I want?
I realize that this is the way the overload looks, and that toArray() currently returns an Object[] instead. But why was this decision made when this part of Java was invented? Why is this design better than, say, skipping the .toArray() that returns Object[]overload entirely and just have a toArray() that returns T[]? Is this a limitation in the compiler, or in the imagination of the designers of this part of the framework, or something else?
As you can probably tell from my extremely keen interest in things of the utmost unimportance, I haven't slept in a while...
No, most of these reasons are wrong. It has nothing to do with "backward compatibility" or anything like that. It's not because there's a method with a return type of Object[] (many signatures were changed for generics where appropriate). Nor is it because taking an array will save it from reallocating an array. They didn't "leave it out by mistake" or made a bad design decision. They didn't include a T[] toArray() because it can't be written with the way arrays work and the way type erasure works in generics.
It is entirely legal to declare a method of List<T> to have the signature T[] toArray(). However, there is no way to correctly implement such a method. (Why don't you give it a try as an exercise?)
Keep in mind that:
Arrays know at runtime the component type they were created with. Insertions into the array are checked at runtime. And casts from more general array types to more specific array types are checked at runtime. To create an array, you must know the component type at runtime (either using new Foo[x] or using Array.newInstance()).
Objects of generic (parameterized) types don't know the type parameters they were created with. The type parameters are erased to their erasure (lower bound), and only those are checked at runtime.
Therefore you can't create an array of a type parameter component type, i.e. new T[...].
In fact, if Lists had a method T[] toArray(), then generic array creation (new T[n]), which is not possible currently, would be possible:
List<T> temp = new ArrayList<T>();
for (int i = 0; i < n; i++)
temp.add(null);
T[] result = temp.toArray();
// equivalent to: T[] result = new T[n];
Generics are just a compile-time syntactic sugar. Generics can be added or removed with changing a few declarations and adding casts and stuff, without affecting the actual implementation logic of the code. Let's compare the 1.4 API and 1.5 API:
1.4 API:
Object[] toArray();
Object[] toArray(Object[] a);
Here, we just have a List object. The first method has a declared return type of Object[], and it creates an object of runtime class Object[]. (Remember that compile-time (static) types of variables and runtime (dynamic) types of objects are different things.)
In the second method, suppose we create a String[] object (i.e. new String[0]) and pass that to it. Arrays have a subtyping relationship based on the subtyping of their component types, so String[] is a subclass of Object[], so this is find. What is most important to note here is that it returns an object of runtime class String[], even though its declared return type is Object[]. (Again, String[] is a subtype of Object[], so this is not unusual.)
However, if you try to cast the result of the first method to type String[], you will get a class cast exception, because as noted before, its actual runtime type is Object[]. If you cast the result of the second method (assuming you passed in a String[]) to String[], it will succeed.
So even though you may not notice it (both methods seem to return Object[]), there is already a big fundamental difference in the actual returned object in pre-Generics between these two methods.
1.5 API:
Object[] toArray();
T[] toArray(T[] a);
The exact same thing happens here. Generics adds some nice stuff like checking the argument type of the second method at compile time. But the fundamentals are still the same: The first method creates an object whose real runtime type is Object[]; and the second method creates an object whose real runtime type is the same as the array you passed in.
In fact, if you try to pass in an array whose class is actually a subtype of T[], say U[], even though we have a List<T>, guess what it would do? It will try to put all the elements into a U[] array (which might succeed (if all the elements happen to be of type U), or fail (if not)) return an object whose actual type is U[].
So back to my point earlier. Why can't you make a method T[] toArray()? Because you don't know the the type of array you want to create (either using new or Array.newInstance()).
T[] toArray() {
// what would you put here?
}
Why can't you just create a new Object[n] and then cast it to T[]? It wouldn't crash immediately (since T is erased inside this method), but when you try to return it to the outside; and assuming the outside code requested a specific array type, e.g. String[] strings = myStringList.toArray();, it would throw an exception, because there's an implicit cast there from generics.
People can try all sort of hacks like look at the first element of the list to try to determine the component type, but that doesn't work, because (1) elements can be null, and (2) elements can be a subtype of the actual component type, and creating an array of that type might fail later on when you try to put other elements in, etc. Basically, there is no good way around this.
The toArray(String[]) part is there because the two toArray methods existed before generics were introduced in Java 1.5. Back then, there was no way to infer type arguments because they simply didn't exist.
Java is pretty big on backward compatibility, so that's why that particular piece of the API is clumsy.
The whole type-erasure thing is also there to preserve backward compatibility. Code compiled for 1.4 can happily interact with newer code that contains generics.
Yes, it's clumsy, but at least it didn't break the enormous Java code base that existed when generics were introduced.
EDIT: So, for reference, the 1.4 API is this:
Object[] toArray();
Object[] toArray(Object[] a);
and the 1.5 API is this:
Object[] toArray();
T[] toArray(T[] a);
I'm not sure why it was OK to change the signature of the 1-arg version but not the 0-arg version. That seems like it would be a logical change, but maybe there's some complexity in there that I'm missing. Or maybe they just forgot.
EDIT2: To my mind, in cases like this Java should use inferred type arguments where available, and an explicit type where the inferred type is not available. But I suspect that would be tricky to actually include in the language.
Others have answered the "why" question (I prefer Cameron Skinner's answer), I will just add that you don't have to instantiate a new array each time and it does not have to be empty. If the array is large enough to hold the collection, it will be used as the return value. Thus:
String[] asArray = someStrings.toArray(new String[someStrings.size()])
will only allocate a single array of the correct size and populate it with the elements from the Collection.
Furthermore, some of the Java collections utility libraries include statically defined empty arrays which can be safely used for this purpose. See, for example, Apache Commons ArrayUtils.
Edit:
In the above code, the instantiated array is effectively garbage when the Collection is empty. Since arrays cannot be resized in Java, empty arrays can be singletons. Thus, using a constant empty array from a library is probably slightly more efficient.
That's because of type erasure. See Problems with type erasure in the Wikipedia article about Java generics: the generic type information is only available at compile time, it is completely stripped by the compiler and is absent at runtime.
So toArray needs another way to figure out what array type to return.
The example provided in that Wikipedia article is quite illustrative:
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if (li.getClass() == lf.getClass()) // evaluates to true <<==
System.out.println("Equal");
According to Neal Gafter, SUN simply did not have enough resources like MS did.
SUN pushed out Java 5 with type erasure because making generic type info available to runtime means a lot more work. They couldn't delay any longer.
Note: type erasure is not required for backward compatibility - that part is handled by raw types, which is a different concept from reifiable types. Java still has the chance to remove type erasure, i.e. to make all types reifiable; however it's not considered urgent enough to be on any foreseeable agenda.
Java generics are really something that doesn't exist. It's only a syntactic (sugar? or rather hamburger?) which is handled by compiler only. It's in reality only short(?)cut to class casting, so if you look into bytecode you can be at first a bit suprised... Type erasure, as noted in post above.
This shortcut to class casting seemed to be a good idea when operating on Collections. In one time you could at least declare what type of element you're storing, and the programmer-independent mechanism (compiler) will check for that. However, using reflection you can still put into such collection whatever you want :)
But when you do generic strategy, that works on generic bean, and it is put into generic service generized(?) from both bean and strategy, taking generic listener for generic bean etc. you're going stright into generic hell. I've once finished with four (!) generic types specified in declaration, and when realized I need more, I've decided to un-generize the whole code because I've run into problems with generic types compliance.
And as for diamond operator... You can skip the diamond and have exactly the same effect, compiler will do the same checks and generate the same code. I doubt in next versions of java it would change, because of this backward compatibility needed... So another thing that gives almost nothing, where Java has much more problems to deal with, e.g. extreme inconvienience when operating with date-time types....
I can't speak to the JDK team's design decisions, but a lot of the clumsy nature of Java generics comes from the fact that generics were a part of Java 5 (1.5 - whatever). There are plenty of methods in the JDK which suffer from the attempt to preserve backwards compatibility with the APIs which pre-dated 1.5.
As for the cumbersome List<String> strings = new ArrayList<String>() syntax, I wager it is to preserve the pedantic nature of the language. Declaration, not inference, is the Java marching order.
strings.toArray( new String[0] );? The type cannot be inferred because the Array is itself considered a generic class (Array<String>, if it were exposed).
All in all, Java generics mostly exist to protect you from typing errors at compile-time. You can still shoot yourself in the foot at runtime with a bad declaration, and the syntax is cumbersome. As happens a lot, best practices for generics use are the best you can do.
private ArrayList<String> colors = new ArrayList<String>();
Looking at the example above, it seems the main point of generics is to enforce type on a collection. So, instead of having an array of "Objects", which need to be cast to a String at the programmer's discretion, I enforce the type "String" on the collection in the ArrayList. This is new to me but I just want to check that I'm understanding it correctly. Is this interpretation correct?
That's by far not the only use of generics, but it's definitely the most visible one.
Generics can be (and are) used in many different places to ensure static type safety, not just with collections.
I'd just like to mention that, because you'll come accross places where generics could be useful, but if you're stuck with the generics/collections association, then you might overlook that fact.
Yes, your understanding is correct. The collection is strongly-typed to whatever type is specified, which has various advantages - including no more run-time casting.
Yeah, that's basically it. Before generics, one had to create an ArrayList of Objects. This meant that one could add any type of Object to the list - even if you only meant for the ArrayList to contain Strings.
All generics do is add type safety. That is, now the JVM will make sure that any object in the list is a String, and prevent you from adding a non-String object to the list. Even better: this check is done at compile time.
Yes. To maintain type safety and remove runtime casts is the correct answer.
You may want to check out the tutorial in the Java site. It gives a good explanation of in the introduction.
Without Generics:
List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3
With Generics
List<Integer> myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'
I think it as type safety and also saving the casting. Read more about autoboxing.
You can add runtime checks with the Collections utility class.
http://java.sun.com/javase/6/docs/api/java/util/Collections.html#checkedCollection(java.util.Collection,%20java.lang.Class)
Also see checkedSet, checkedList, checkedSortedSet, checkedMap, checkedSortedMap
Yes, you are correct. Generics adds compile-time type safety to your program, which means the compiler can detect if you are putting the wrong type of objects into i.e. your ArrayList.
One thing I would like to point out is that although it removes the visible run-time casting and un-clutters the source code, the JVM still does the casting in the background.
The way Generics is implemented in Java it just hides the casting and still produces non-generic bytecode. An ArrayList<String> still is an ArrayList of Objects in the byte-code. The good thing about this is that it keeps the bytecode compatible with earlier versions. The bad thing is that this misses a huge optimization opportunity.
You can use generic anywhere where you need a type parameter, i.e. a type that should be the same across some code, but is left more or less unspecified.
For example, one of my toy projects is to write algorithms for computer algebra in a generic way in Java. This is interesting for the sake of the mathematical algorithms, but also to put Java generics through a stress test.
In this project I've got various interfaces for algebraic structures such as rings and fields and their respective elements, and concrete classes, e.g. for integers or for polynomials over a ring, where the ring is a type parameter. It works, but it becomes somewhat tedious in places. The record so far is a type in front of a variable that spans two complete lines of 80 characters, in an algorithm for testing irreducibility of polynomials. The main culprit is that you can't give a complicated type its own name.