Search code examples
javagenericspolymorphismgeneric-programming

How can you enforce that a generic type is enclosed in a specific class?


I came across an interesting problem recently: I want a method in a generics-class to accept only instances of an inner class of the specified generic type only. The goal, illustrated in the following example, is that a CarInspector<CarBrand1> can only inspect driving wheels manufactured by CarBrand1, while keeping DrivingWheel an inner class of CarMaker. Ther rationale being that I don’t want the DrivingWheel be defined outside an enclosing CarMaker subclass.

This was my first approach to the problem:

public abstract class CarMaker {
    public abstract class DrivingWheel {}
}

public class CarBrand1 extends CarMaker {
    public class DrivingWheelByCarBrand1 extends DrivingWheel {}

    private final DrivingWheelByCarBrand1 drivingWheel = new DrivingWheelByCarBrand1();

    public DrivingWheelByCarBrand1 getDrivingWheel() {
        return drivingWheel;
    }
}

public class CarBrand2 extends CarMaker {
    private class DrivingWheelByCarBrand2 extends DrivingWheel {}

    private final DrivingWheelByCarBrand2 drivingWheel = new DrivingWheelByCarBrand2();

    public DrivingWheelByCarBrand2 getDrivingWheel() {
        return drivingWheel;
    }
}

public class CarInspector<T extends CarMaker> {
    public void inspect(T.DrivingWheel arg) {}
}

However, the following code compiles just fine:

    CarBrand1 c1 = new CarBrand1();
    CarBrand2 c2 = new CarBrand2();

    CarInspector<CarBrand1> carInspector = new CarInspector<>();
    carInspector.inspect(c1.getDrivingWheel());
    carInspector.inspect(c2.getDrivingWheel());

Is there a way so that the compiler throws an error or warning in the line carInspector.inspect(c2.getDrivingWheel())?

NOTE: this question is besides the fact that the proposed design might not be the best choice and that defining DrivingWheel as DrivingWheel<T extends CarMaker> might be more sensible; it’s an interesting tangent nonetheless, and it’s a design alternative I’m considering for the specific domain I’m working at.


Solution

  • You need to use more generics. Change the base DrivingWheel class to add a generic parameter that specifies the car brand:

    public class CarInspector<T extends CarMaker> {
        public void inspect(DrivingWheel<T> arg) {}
    }