Differencebetween ? and Object in Java Generics

What is the difference between ? and Object in Java generics?

An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write

public void foobar(Map<String, Object> ms) {
...
}

you can't supply a HashMap<String, String>. If you write

public void foobar(Map<String, ?> ms) {
...
}

it works!

A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).

Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write

public void foobar(List<? extends InputStream> ms) {
...
}

By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)

What is difference between Generic type and Object in method declaration?

Like that there is no difference, for T you are using the type twice so you have a reason to use it but for J it is only used once.

If it really can be any object at all and you never use that type again then there is no reason to use generics for it. Generics allow you to take the return type and parameters of the method and link 2 or more of them together. They also allow you to link together multiple methods when using generics in a class definition.

Neither of those use cases applies here.

Java generics T vs Object

Isolated from context - no difference. On both t and obj you can invoke only the methods of Object.

But with context - if you have a generic class:

MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();

Then:

Foo newFoo = my.doSomething(foo);

Same code with object

Foo newFoo = (Foo) my.doSomething(foo);

Two advantages:

  • no need of casting (the compiler hides this from you)
  • compile time safety that works. If the Object version is used, you won't be sure that the method always returns Foo. If it returns Bar, you'll have a ClassCastException, at runtime.

What is the difference between type T and Object?

At runtime, there is no difference: Java generics are implemented through Type Erasure, so the same class is used in all implementations.

At compile time, however, the difference is enormous, because it lets you avoid casting every time that you use an object, making you code look a lot cleaner.

Consider this example:

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
for (Integer n : list) {
System.out.println(n+5);
}

This compiles and runs well, and it also easy to read. If you wanted to use List<Object> instead, the code would not look as clean:

List<Object> list = new ArrayList<Object>();
list.add(1);
list.add(2);
list.add(3);
for (Object o : list) {
// Now an explicit cast is required
Integer n = (Integer)o;
System.out.println(n+5);
}

Internally, though, the two code snippets use the same exact implementation for their list object.

What's the difference between ? and ? extends Object in Java Generics?

<?> and <? extends Object> are synonymous, as you'd expect.

There are a few cases with generics where extends Object is not actually redundant. For example, <T extends Object & Foo> will cause T to become Object under erasure, whereas with <T extends Foo> it will become Foo under erasure. (This can matter if you're trying to retain compatibility with a pre-generics API that used Object.)

Source: http://download.oracle.com/javase/tutorial/extra/generics/convert.html; it explains why the JDK's java.util.Collections class has a method with this signature:

public static <T extends Object & Comparable<? super T>> T max(
Collection<? extends T> coll
)

Java generics, Unbound wildcards ? vs Object

There are two separate issues here. A List<Object> can in fact take any object as you say. A List<Number> can take at least Number objects, or of course any subclasses, like Integer.

However a method like this:

public void print(List<Number> list);

will actually only take a List which is exactly List<Number>. It will not take any list which is declared List<Integer>.

So the difference is List<?> will take any List with whatever declaration, but List<Object> will only take something that was declared as List<Object>, nothing else.

The last quote simply states, that List<?> is a list for which you literally don't know what type its items are. Because of that, you can not add anything to it other than null.

Generics vs Object vs Comparable

The conceptual thing to know about: Java arrays are covariant!

That means: you can write a method like

public void sort(Object[] data)

and use that with an array of Objects, but also with an Integer[], String[] whatever.

That has the advantage that you can write code that works "generically" for all kinds of different input.

But the problem with that is that it can lead to surprises at runtime, for example when your array contains Integer and String objects.

Thus the Java language folks decides to make generics, and more specifically collections of Generics invariant. Therefore you can't do

public void sort(List<Number> numbers)

and call that with some List<Integer>.

In that sense: when using arrays, then there isn't much sense in using generics. But: when using generics, you would (most of the time) prefer using collections over arrays! And then, you have to really know about the conceptual differences.

Java generics, objects and wildcards differences & clarifications

I will list the main differences between T and ?:

  • Basic: T is a type parameter and ? is a wildcard.

  • Meaning: T is used as a type parameter when defining a generic class. T will be replaced by a concrete type when you instantiate the generic class.
    On the other hand, we use ? when we want to refer to an unknown type argument.

  • Place of definition: You need to declare T on the top of the class, or method if you define a generic method. You can use ? everywhere.
  • Mapping: Every use of T is mapped to the same type (in the same class). Every use of ? can be mapped to a different type.
  • Object instantiation: You can create objects with the generic parameter T like new ArrayList<T>(). You cannot instantiate objects but only pointers with ?.
  • Collections updating: You can add objects to a collection of type T. You cannot add object to a collection of type ? (since you don't know its type).

  • Type erasures: With generics, type erasure applies to the use of generics. when generics are used, they are converted into compile-time checks and execution-time casts. So if you have this code for example: List<String> myList = new ArrayList<String>(); and then you wish to add to your list so you do myList.add("Hello World"); and then you want to get the item you just added by performing String myString = myList.get(0); then the compiler will compile your code to List myList = new ArrayList(); and String myString = (String) myList.get(0); (the add stays the same for obvious reasons).

    So basically, at execution time there is no way of finding out that T is essentially String for the list object (that information is gone).


    Now for wildcards the story is different. A wildcard (?) is replaced by an Object (since it's unbounded). This is not very useful. At build-time the compiler will check you are only calling Object's behaviours. If you have something like ? extends Foo, then the ? is replaced with its bound Foo (at build-time the compiler will check you are only passing Foo or any of its subtypes (types that inherit from Foo) as an argument).

For differences between ? and Object & T and Object you may read here and here respectively.

Generics: What is the difference between ?, Object, and raw type of a single instance?

The Java Language Specification writes:

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

That leaves the other two. As ScheduledFuture only uses its type parameter in a method return type, ScheduledFuture<?> (which is the same as ScheduledFuture<? extends Object>) is equivalent to ScheduledFuture<Object> to calling code.

However, code that actually creates an instance of a ScheduledFuture needs to work with a subclass of ScheduledFuture, and may care a great deal about the type parameter that subclass implements ScheduledFuture with.

When declaring a method that returns a ScheduledFuture, you will therefore want to use the type ScheduledFuture<?> as that signature is easier to implement for the producer of the ScheduledFuture, but equally useful to its consumer. (This is simply a special case of the PECS rule.)



Related Topics



Leave a reply



Submit