495

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?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
tofarr
  • 7,682
  • 5
  • 22
  • 30

7 Answers7

539

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!

ColinD
  • 108,630
  • 30
  • 201
  • 202
  • In your second block of code this line `LinkedList raw = a;` should cause unchecked warning. It's not the same as the approach suggested in the question. – Roman Nov 12 '10 at 17:17
  • @Roman: It's the same in that the right-hand side is a raw type in both cases. Raw types and generic types should not be mixed, that's all there is to it. – ColinD Nov 12 '10 at 17:20
  • @Roman: I changed the example to show more clearly that there are potentials for problems even when the right-hand side is `new LinkedList`. – ColinD Nov 12 '10 at 17:35
  • 36
    Backward compatibility is great, but not at the expense of complexity please. Why couldn't Java 7 just introduce `-compatibility` compiler switch whereas if absent then `javac` will forbid all raw types and enforce strictly generic types only? This will make our codes less verbose as it is. – Rosdi Kasim Jan 06 '11 at 17:03
  • 3
    @Rosdi: Agreed, there's no need for raw types in new code. However, I'd strongly prefer including the Java version number in the source file (instead of (mis)using the command line), see my answer. – maaartinus Sep 16 '11 at 17:17
  • I'm not 100% following this response I must say for two reasons - first as I understand there is no such thing as a "generic instance" and second it is incorrect that information on the left is used on the right. Take the declaration Object o = new LinkedList<>(); ... there is no information there, there is nothing being "copied" from the left hand side to the right hand side of an assignment. – Mishax Jan 01 '13 at 13:24
  • 43
    I personally don't like the use of diamonds UNLESS you are defining AND instantiating on the same line. `List strings = new List<>()` is OK, but if you define `private List my list;`, and then halfway down the page you instantiate with `my_list = new List<>()`, then it's not cool! What does my list contain again? Oh, let me hunt around for the definition. Suddenly, the benefit of the diamond shortcut goes bye bye. – rmirabelle Apr 03 '13 at 03:00
  • So if it's a parameterless constructor, then using generics on the right is identical to using raw types on the right? – Rag Apr 19 '13 at 21:07
  • 16
    @rmirabelle How is that different from: `my_list = getTheList()`? There are several better ways how to deal with this kind of problems: 1. use an IDE that shows you types of variables upon mouse hover. 2. use more meaningful variable names, such as `private List strings` 3. don't split declaration and initialization of variables unless you really have to. – Natix Oct 24 '14 at 09:35
  • @rmirabelle, Netbeans allows you to ctrl+click the variable to jump to the definition (and a back button to go back), so that should be rarely a problem if you use a good IDE. – ryvantage Mar 08 '15 at 03:42
  • Is that answer still valid for Java 8? I don't get any warnings when I assign raw type variable to the generic type variable. – Morfidon Aug 18 '15 at 14:21
  • 1
    @Morfidon: Yes, it's still valid for Java 8. I'm pretty sure you _should_ be getting warnings. – ColinD Aug 18 '15 at 16:59
  • @Rosdi Backward compatibility applies not just to source code but also to compiled bytecode. – Jonathan Rosenne Dec 30 '17 at 20:53
  • As I understand: After omitting <> in the right side of expression: List integers = new LinkedList(); you don't get raw type LinkedList. You get a generic type of LinkedList. So you won't be able to add object of different than Integer type to that list. The ONLY place where you deal with RAW type is actually the right side of declaration. – Zbyszek Sep 01 '20 at 04:02
40

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.

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
  • 8
    `List list = new LinkedList()` is the correct code. You know this and I know this too. And the question (as I understand it) is: why only java compiler doesn't understand that this code is quite secure? – Roman Nov 12 '10 at 16:57
  • 24
    @Roman: `List list = new LinkedList()` is _not_ the correct code. Sure, it would be nice if it were! And it probably could have been if Java had generics from the beginning and didn't have to deal with backwards compatibility of generic types that used to be non-generic, but it does. – ColinD Nov 12 '10 at 17:41
  • 7
    @ColinD Java really needn't to deal with backwards compatibility in *each single line*. In any Java source file using generics the old non-generic types should be forbidden (you can always use `>` if interfacing to legacy code) and the useless diamond operator should not exist. – maaartinus May 30 '11 at 17:58
15

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'.

Roman
  • 64,384
  • 92
  • 238
  • 332
12

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?

  1. 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.
  2. 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.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 2
    Have you considered the difference between `new ArrayList(anotherList)` and `new ArrayList<>(anotherList)` (especially if it is being assigned to `List` and `anotherList` is a `List`)? – Paul Bellora Mar 13 '13 at 12:12
  • @Paul Bellora: No. Surprisingly for me, both compile. The one with diamonds even given no warning. However, I can't see any sense in this, can you elaborate? – maaartinus Mar 16 '13 at 21:02
  • Sorry, I didn't explain very well. See the difference between these two examples: http://ideone.com/uyHagh and http://ideone.com/ANkg3T I'm just pointing out that it does matter to use the diamond operator instead of a raw type, at least when arguments with generic bounds are being passed in. – Paul Bellora Mar 17 '13 at 01:29
  • Actually I hadn't taken the time to read ColinD's answer - he cites pretty much the same thing. – Paul Bellora Mar 17 '13 at 15:35
  • I made a comment in the blog where `<` and `>` got filtered out making it hard to get the meaning. What I wanted to say is, that if I write line like `List list=new ArrayList();`, there is no sane reason why the compiler assumes that I want a *raw type*, an artifact that exist to support pre-Generics code, while the type `List` of the variable clearly shows that this isn’t pre-Generics code (and explicitly says that I do *not* want a raw type as otherwise I had written `List list=…`) – Holger May 28 '15 at 14:25
  • @Holger Yes, this makes sense. It may get more complicated with `f(new List())`, but I guess it's safe to assume that you actually never want a row type, unless you explicitly ask for it (e.g. with your `List list=`). I'm not sure about all the edge cases, but I'm with you. +++ Nonetheless, assuming that a special syntax is needed and that it's me to invent a syntax for a raw type constructor, I'd use `new List<>()`. :D – maaartinus May 28 '15 at 14:45
  • Well, Java 8 would infer the type from `f`’s parameter type which indeed might be generic while the caller might be legacy but it’s hard to imagine, how an enforced type inference could create problems here, unless the types are really incompatible. On the other hand, `f(Arrays.asList(new UnmatchingType[num]))` will fail as well when migrating from pre-Generic code to Generic instead of resorting to raw types. It’s again the big old question why Generics treats constructors differently than methods. – Holger May 28 '15 at 15:41
  • 2
    So, if we are about to introduce a new syntax for raw types, for the few places where it is really needed, why not use something like `new @RawType List()`. That’s already valid Java 8 syntax and type annotations allow to use it at every place where needed, e.g. `@RawType List = (@RawType List) genericMethod();`. Considering that raw types currently create a compiler warning unless an appropriate `@SuppressWarnings` has been placed, `@RawType` would be a reasonable replacement and a more subtle syntax isn’t needed. – Holger May 28 '15 at 15:49
8

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.

axtavt
  • 239,438
  • 41
  • 511
  • 482
4

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.

allprog
  • 16,540
  • 9
  • 56
  • 97
2

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.

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • you don't even have to use that annotation. At least in Eclipse, you can just tell the compiler to not warn you about this and you are fine... – xeruf Jul 17 '17 at 17:01
  • It's better to have the annotation. Not every developers uses Eclipse out here. – Buhake Sindi Jul 18 '17 at 10:34