Skip to content
Advertisement

Using Generics for return type parameter Java

I am trying to create a method that will return a generic type parameter.

I have a class VehicleOrder that extends the abstract class Order. In class Order, I created an abstract method receiveHiredObject. This method will not receive any parameter and will return a generic.

public abstract class Order implements Orderable {

private Date date;
private Customer customer;

public abstract <T> T receiveHiredObject();

I implemented this method in the class VehicleOrder and I set it up to return the class parameter vehicle.

public class VehicleOrder extends Order {

private Vehicle vehicle;

public VehicleOrder(final Date date, final Customer customer, final Vehicle vehicle) {
    super(date, customer);
    this.vehicle = vehicle;
}

@SuppressWarnings("unchecked")
@Override
public Vehicle receiveHiredObject() {
    return vehicle;
}

The problem is that when I’m instantiating a new VehilceOrder of the type Order and, am using the method receiveHiredObject, this method is returning me an Object and I have available only the method toString.

Order a = new VehicleOrder();
a.receiveHiredObject();

I want that in the future to use the same method for other types of objects like HotelOrder.

What I’m doing wrong, why does the method receiveHiredObject is returning an Object instead of a VehicleOrder? How can I improve the method to return the right type?

Thank you,

Advertisement

Answer

Note that receiveHiredObject does “return the right type”, if you tell it what type to return.

Order order = new VehicleOrder();
Vehicle v = order.receiveHiredObject();

It returns Object because without you specifying a type, the type parameter T gets inferred to be Object, because T is only bounded by Object.

However, your method is not safe at all! You can do silly things like:

Order order = new VehicleOrder();
Hotel h = order.receiveHiredObject();

And this will compile, but crash when you run it.

This is because you declared receiveHiredObject to return a T – the generic parameter. This means that whatever T that the caller specifies – whether it is Vehicle, Hotel, or Foo, receiveHiredObject is declared to return that type of thing. Clearly, it doesn’t do that. What it returns depends on the subclass used. The caller doesn’t get to decide what it returns! When you implement receiveHiredObject in VehicleOrder, you received a warning because returning a Vehicle might not be the T that the caller expects and the runtime cannot check this for you, but you suppressed the warning.

So receiveHiredObject should not be generic at all. You can instead make Order generic:

// might have to change Orderable too, if receiveHiredObject comes from there
public abstract class Order<T> implements Orderable {

    private Date date;
    private Customer customer;

    public abstract T receiveHiredObject();
}

public class VehicleOrder extends Order<Vehicle> {
    // same code as before...
}

Now the caller can do:

Order<Vehicle> order = new VehicleOrder();
order.receiveHiredObject().someVehicleSpecificThing();

Now it is much safer – you won’t be able to get hotels from a vehicle order.

Note that it is absolutely necessary to provide the type information of Vehicle. This is because what order.receiveHiredObject returns is determined by the runtime type of order, and the compiler has no way of knowing what that is. Think in the extreme case:

Order order = new Random().nextBoolean() ? new VehicleOrder() : new HotelOrder();
// what type should order.receiveHiredObject return here?
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement