Students who like to study may notice that in other people’s code, sometimes Object is returned, and sometimes a
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
<?> 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
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.
