3

I've been going over Generic classes and methods in Java and came across this question in a past paper

enter image description here

I tried implementing the interface and classes proposed in the question, including the refuel method, and found no problem with passing the Car argument as a parameter

Motorized interface

public interface Motorized {

    public int getFuel();

}

Vehicle class

public abstract class Vehicle{

    private int fuel;

    public Vehicle(int fuel) {

        this.fuel = fuel;
    }

    public String toString(){

        return("Fuel is: " + getFuel());
    }

    public int getFuel() {

        return fuel;
    }
}

Car class

public class Car extends Vehicle implements Motorized {

    int seats;

    public Car(int fuel, int seats) {

        super(fuel);
        this.seats = seats;
    }

    public int getSeats() {

        return seats;
    }

    @Override
    public String toString(){

        return("Fuel is: " + getFuel() + "and the car has" + getSeats() + "seats.");
    }
}

test method

public class VehicleTest {

    public static Motorized refuel(Motorized v) {

        return v;
    }

    public static void main(String[] args) {

        Car car = new Car(15, 5);

        System.out.println(refuel(car));
    }
}

Can somebody explain to me what the problem should be with this question and why my implementation of it doesn't reflect this problem?

3 Answers 3

2

The problem lies with the return value of the method:

public static Motorized refuel(Motorized v)

You said you had no problems passing in a Car and that is a completly valid statement. But you have not tried to get the value back from the refuel method:

Car car = ...
Car refueled = refuel(car); // compiler error: Motorized is not assignable to Car!

The return type is Motorized although Car extends Motorized, you can't be sure that the Motorized instance that is returned will always be a Car. See this simplified example:

public static Motorized refuel(Motorized v) {
    // try to refuel
    // uh oh... the gas station exploded
    // you have to change to a motorbike which just stands around
    return new Motorbike();
}

Now you maybe expect a Car but you get a Motorbike, so even a cast fails:

Car refueled = (Car) refuel(car); // ClassCastException, Motorbike is not a Car

You can do this with generics though:

public static <M extends Motorized> M refuel(M m) {
    // refuel the motorized vehicle
    return m;
}

Though if the gas station explodes again, then the method has a problem. It doesn't know what M actually is. So this can be a lifesaver for headaches to you.

Sign up to request clarification or add additional context in comments.

1 Comment

Great, that makes sense. Thanks!
1

Instead of

System.out.println(refuel(car));

try

Car refueledCar = refuel(car);

You'll find that it doesn't compile, because the variable that you get back from the refuel method isn't an instance of Car, but is rather some sort of object (but the compiler doesn't know which sort of object) that implements Motorized. You can cast it to a Car with

Car refueledCar = (Car) refuel(car);

But that's pretty clunky, and makes you code a bit more prone to bugs. Fortunately, there are better ways to address this. Specifically, by using generics. If you change the method signature for refuel to this:

public static <T extends Motorized> T refuel(T t)

you'll find that the type you get back is known to be the same as the type you pass in, and the compiler is perfectly happy with the line above that previously failed to compile.

By way of explanation, that new method signature basically means "There is some unknown type, T, which extends Motorized. The method takes in a variable of type T, and has a return value of the same type."

Comments

0

You can use a generic method for this.

Here's an ultra-simplified hierarchy (Vehicle disregarded here).

interface Motorized {
    void refuel();
}
class Car implements Motorized {
    @Override
    public void refuel() {
        // TODO
    }
}

Now here's your test class:

class VehicleTest {
//         | generic method type parameter bound up to any `Motorized`
//         |                     | returns same type...
//         |                     |        | ... as given type
//         |                     |        |
    static <T extends Motorized> T refuel(T t) {
        t.refuel();
        return t;
    }

    // and here's some usage
    public static void main(String[] args) {
        Car car = new Car();
        Car refueled = refuel(car);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.