25

I am reading the chapter on Generics in Effective Java.

Help me understand difference between Set, Set<?> and Set<Object>?

The following paragraph is taken from the book.

As a quick review, Set<Object> is a parameterized type representing a set that can contain objects of any type, Set<?> is a wildcard type representing a set that can contain only objects of some unknown type, and Set is a raw type, which opts out of the generic type system.

What is meant by "some unknown type"? Are all unknown types of type Object? In that case what is the specific difference between Set<?> and Set<Object>?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Vinoth Kumar C M
  • 10,378
  • 28
  • 89
  • 130

8 Answers8

20
  • a raw type (Set) treats the type as if it had no generic type information at all. Note the subtle effect that not only will the type argument T be ignored, but also all other type arguments that methods of that type might have. You can add any value to it and it will always return Object.
  • Set<Object> is a Set that accepts all Object objects (i.e. all objects) and will return objects of type Object.
  • Set<?> is a Set that accepts all objects of some specific, but unknown type and will return objects of that type. Since nothing is known about this type, you can't add anything to that set (except for null) and the only thing that you know about the values it returns is that they are some sub-type of Object.
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • So is a raw type (`set`) the same as a set that accepts `Object` types (`Set `)? – orrymr Jul 25 '16 at 08:13
  • @orrymr: it will work very similarly, but it's explicitly not assignment-compatible (and some corner-cases are actually different). Also, technically it's not always `Object`, but the lower bound of the generic type (i.e. if the generic type is just ``, then it's `Object`, but if the generic type is `` then the lower bound is `Number`). – Joachim Sauer Jul 25 '16 at 09:39
  • Not assignment compatible, so you couldn't say: `Set mySet = new Set ;` ? Would this be because `Set ` does _not_ inherit from `Set `? – orrymr Jul 25 '16 at 13:17
3

At runtime, the JVM will just see Set because of type erasure.

At compile-time, there's a difference:

Set<Object> parameterized a type E with Object thus, Set.add(E element) will be parameterized to Set.add(Object element).

Set<?> on the other hand, adds a wildcard on a type E so Set.add(E element) is translated to Set.add(? element). Since this is not compilable, java instead "translates" it as Set.add(null element). It means that you cannot add anything to that set (except a null). The reason is that the wildcard is referencing to an unknown type.

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • Though Set, is parameterized to Set.add(Object element), it is not possible to add a sub type of Object(eg. String) right ? – Vinoth Kumar C M Sep 09 '11 at 11:06
  • It is possible since **all concrete** classes implicitly/explicitly extends `Object`. – Buhake Sindi Sep 09 '11 at 11:08
  • So why does this code not compile ?Set set = new HashSet(); – Vinoth Kumar C M Sep 09 '11 at 11:10
  • Because you have instantiated your set wrong. The instance `set` must have the **same type** `T` of which you assigned. The correct thing is to do `Set set = new HashSet();`. Yours can be interpreted as `Set set = new HashSet()` (`T` != `U` in Generics term). – Buhake Sindi Sep 09 '11 at 11:13
3

what is meant by "some unknown type"

Exactly what it means - the Set has some generic parameter, but we don't know what it is.

So the set assigned to a Set<?> variable might be a Set<String>, or a Set<Integer>, or a Set<Map<Integer, Employee>> or a set containing any other specific type.

So what does that mean for how you can use it? Well, anything you get out of it will be an instance of the ?, whatever that is. Since we don't know what the type parameter is, you can't say anything more specific than that elements of the set will be assignable to Object (only because all classes extend from it).

And if you're thinking of adding something to the set - well, the add method takes a ? (which makes sense, since this is the type of objects within the set). But if you try to add any specific object, how can you be sure this is type-safe? You can't - if you're inserting a String, you might be putting it into a Set<Integer> for example, which would break the type-safety you get from generics. So while you don't know the type of the generic parameter, you can't supply any arguments of this type (with the single exception of null, as that's an "instance" of any type).


As with most generics-related answers, this has focused on collections because they're easier to comprehend instinctively. However the arguments apply to any class that takes generic parameters - if it's declared with the unbounded wildcard parameter ?, you can't supply any arguments to it, and any values you receive of that type will only be assignable to Object.

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
2

Set: No generics here, unsafe. Add what you want.

Set<?>: A set of a certain type we don't know from our scope. The same as Set<? extends Object>. Can reference to Sets of any type, but that type must be defined at the point where the set is actually instantiated. With the wildcarded reference, we can´t modify the set (we can't add or remove with anything not null). Is like a view.

Set<Object>: Set containing Objects (base class only, not subclasses). I mean you can instance the set using Collections of type Object, like HashSet<Object> but not with HashSet<String>. You can of course add elements of any type to the set, but just because it happens that everything is an Object or a subclass of Object. If the set were defined as Set, you can only add Numbers and subclasses of Number, and nothing more.

Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • 3
    A `Set` can *definitely* contain any subclass of `Object`, you can add `Integer`, `String` and everything else to it. – Joachim Sauer Sep 09 '11 at 11:03
  • So why does this code not compile ?Set set = new HashSet() – Vinoth Kumar C M Sep 09 '11 at 11:13
  • @cmv Because you are defining the set reference as a Set of type Object and nothing more. You should have written `Set extends Object>` in order to compile. Notice that it is possible to write `Object[] arr = new Integer[3];` with arrays. – Mister Smith Sep 09 '11 at 11:25
  • 2
    @cmv the reason the compiler prevents you from assigning `HashSet` to `Set` or even `HashSet` is because holding that reference you would add something that is not an Integer. – Mister Smith Sep 09 '11 at 11:34
1

I was explaining this item to my friend and specifically asked for the "safeAdd" method as a counter to the unsafeAdd example. So here it is.

public static void main(String[] args) {
    List<String> strings = new ArrayList<String>();

    unsafeAdd(strings, new Integer(42)); // No compile time exceptions

    // New 
    safeAdd(strings, new Integer(42)); // Throwing an exception at compile time


    String s = strings.get(0); // Compiler-generated cast

}

private static void unsafeAdd(List list, Object o) {
    list.add(o);
}


private static <E> void safeAdd(List<E> list, E o) {
    list.add(o);
}
th3byrdm4n
  • 172
  • 1
  • 10
1

The difference between Set<Object> and Set<?> is that a variable of type Set<?> can have a more specific generic type assigned to it, as in:

Set<?> set = new HashSet<Integer>();

while Set<Object> can only be assigned Set<Object>:

Set<Object> set = new HashSet<Integer>(); // won't compile

The Set<Object> is still useful, since any object can be put into it. It is much like the raw Set in that sense, but works better with the generic type system.

Avi
  • 19,934
  • 4
  • 57
  • 70
1
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error

Since we don’t know what the element type of set stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the Set. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don’t know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

Given a Set<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

Holger
  • 285,553
  • 42
  • 434
  • 765
Narendra Yadala
  • 9,554
  • 1
  • 28
  • 43
-1

Let say,you are writing a common method to print the elements appearing in a List. Now, this method could be used for printing Lists of type Integer, Double,Object or any other type. Which one do you choose?

  1. List<Object> : If we use this one, this would help us to print only the elements of type Object. It wont be useful for printing elements belonging to other classes like Double. This is because Generic does not support Inheritance by default and needs to be specified explicitely using 'super' keyword.

    // Would only print objects of type 'Object'
    
    public static void printList(List<Object> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }
    
  2. List<?> : This could help us have a common method for printing any datatype. We could use this method for printing instances of any type.

    //  The type would really depend on what is being passed
    public static void printList(List<?> list) {
        for (Object elem: list)
            System.out.print(elem + " ");
        System.out.println();
    }
    
Ishita Sinha
  • 2,168
  • 4
  • 25
  • 38