Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java, Generics: What's the difference between Set<?> s = HashSet<String>() and Set s = HashSet<String>()? [duplicate]

Tags:

java

generics

I was reading about unknown types and raw types in generics, and this question came to mind. In other words, is...

Set<?> s = new HashSet<String>();

and

Set s = new HashSet<String>();

... one and the same?

I tried it out, and they both seem to accomplish the same thing, but I would like to know if they are any different to the compiler.

like image 698
Srikanth Avatar asked Feb 26 '14 17:02

Srikanth


4 Answers

No, they are not the same. Here's the basic difference:

Set<?> s = HashSet<String>();
s.add(2);  // This is invalid

Set s = HashSet<String>();
s.add(2);  // This is valid.

The point is, the first one is a unbounded parameterized type Set. Compiler will perform the check there, and since you can't add anything but null to such types, compiler will give you an error.

While the second one being a raw type, the compiler won't do any check while adding anything to it. Basically, you lose the type safety here.

And you can see the result of loosing type safety there. Adding 2 to the set will fail at compile time for Set<?>, but for a raw type Set, it will be successfully added, but it might throw exception at runtime, when you get the element from the set, and assign it to say String.

Differences apart, you should avoid using raw types in newer code. You would rarely find any places where you would use it. Few places where you use raw type is to access static fields of that type, or getting Class object for that type - you can do Set.class, but not Set<?>.class.

like image 124
Rohit Jain Avatar answered Sep 23 '22 20:09

Rohit Jain


The first one create a Set<?>, which means: "a generic Set of some unknown class". You won't be able to add anything to this set (except null) because the compiler doesn't know what its generic type is.

The second creates a raw, non generic set, and you can add anything you want to it. It doesn't provide any type-safety.

I don't see why you would use any of them. Set<String> should be the declared type.

like image 37
JB Nizet Avatar answered Sep 22 '22 20:09

JB Nizet


The first one uses generics and the second one uses the raw form of Set.

The first one uses a wildcard as the generic type parameter. It means, "a Set of some specific yet unknown type", so you won't be call methods such as add that take a generic parameter, because the compiler doesn't know which specific type it really is. It maintains type safety by disallowing such a call at compile time.

The raw form removes all generics and provides no strong typing. You can add anything to such a Set, even non-Strings, which makes the following code not type-safe:

Set<String> genericSet = new HashSet<String>();
Set rawSet = genericSet;
rawSet.add(1);  // That's not a String!

// Runtime error here.
for (String s : genericSet)
{
    // Do something here
}

This would result in a runtime ClassCastException when the Integer 1 is retrieved and a String is expected.

Maintaining as much generic type information as possible is the way to go.

Set<String> s = HashSet<String>();
like image 20
rgettman Avatar answered Sep 22 '22 20:09

rgettman


Set<?> tells the compiler that the set contains a specific type, but the type is unknown. The compiler uses this information to provide errors when you attempt to invoke a method with a generic parameter, like add(T).

Set tells the compiler that the set is a "raw" type, where no generic type parameter is given. The compiler will raise warnings, rather than errors, when the object's generic methods are invoked.

In order to add elements to the set without warnings, you need to specify the generic type information on the variable. The compiler can infer the type parameters for the constructor. Like this:

Set<String> s = new HashSet<>();

This information allows the compiler to verify that the Set is used in a type safe way. If your code compiles without type safety warnings, and you don't use any explicit casts, you can be assured that there will be no ClassCastException raised at runtime. If you use generics, but ignore type safety warnings, you might see a ClassCastException thrown at a point where you don't have a cast in your source code.

like image 28
erickson Avatar answered Sep 22 '22 20:09

erickson