Can't Tell Object, Generic <T>, and Wildcard <?> Apart in Java? Here's the Clarification

Students who like to study may notice that in other people's code, sometimes Object is returned, and sometimes a <T> generic is used. Code using <T> generics may also contain symbols like <?>. From vague memories of school years ago, you might roughly understand that Object is the parent of all types and can represent any type, and that generics don't restrict the type and can also represent all types — at which point you get confused. Today I'll make their differences clear.

Students who like to study may notice that in other people’s code, sometimes Object is returned, and sometimes a generic is used. Code using generics may also contain symbols like <?>. From vague memories of school years ago, you might roughly understand that Object is the parent of all types and can represent any type, and that generics don’t restrict the type and can also represent all types — at which point you get confused. Today I’ll make their differences clear.

Object

Object is a big shot in Java — everything is an object and all originate from Object, so Object can hold any object. You could say Object is the world, but whether it’s a human or a ghost can only be known at runtime. So once a casting error occurs, it cannot be caught at compile time.

Generics

As the name suggests, it is a generalized type. You declare the generic when defining, and specify a concrete type when using it, so type-casting errors can be checked at compile time. For example, the List class declares a generic; when using it we must specify a concrete type like List, which makes clear what kind of things are in the collection, facilitating code checking and cast safety.

<?> Wildcard

If you use generics but don’t yet know, inside your code, what type the generic is — yet want to constrain it — you can use <?> to represent it. For example, to restrict it to a subtype of Number, use List<? extends Number>; for a supertype of Number, use List<? extends Number>.

A Case to Deepen Understanding

Let’s first declare two methods, one using Object and one using generics, and feel the difference:

public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}

Both methods can receive any type of object and return any object, but differences appear in use. For example:

MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
// Using public <T> T doSomething(T t)
Foo newFoo = my.doSomething(foo);
// Using public Object doSomething(Object obj)
Foo newFoo = (Foo) my.doSomething(foo);

Notice the difference? With the generic method, when writing code you already know what it will return, while the Object method still needs a cast, and sometimes you only know the type at runtime. Generics require no cast and are safer with compile-time type checking.