Reputation: 316
I'm unable to correctly define a generic method when using covariance on a generic class, if this is at all possible the way I intend to. I'll best explain the issue at hand by example.
Say we have the following setup of interfaces for cars
interface Car { ... }
interface SportsCar extends Car { ... }
and such generic interfaces for car vendors returning a Sale
object
interface CarVendor<C extends Car> {
Sale<C> sell(C car);
}
interface SportsCarVendor extends CarVendor<SportsCar> {
@Override
Sale<SportsCar> sell(SportsCar car);
}
Let's now suppose we want our cars to be generic, e.g. regarding fuel type:
interface Car<F extends FuelType> { ... }
interface SportsCar<F extends FuelType> extends Car<F> { ... }
class PetrolSportsCar extends SportsCar<Petrol> { ... }
class DieselSportsCar extends SportsCar<Diesel> { ... }
We run into problems when redefining our vendor interfaces if we wan't them to be able to sell cars for any kind of fuel. A generic method seem to be the answer, however I'm unable to correctly define it since the generic Car<?>
is defined on the class but the generic FuelType
should be defined on the method. To get the idea:
interface CarVendor<C extends Car<?>> {
<F extends FuelType> Sale<Car<F>> sell(Car<F> param);
}
interface SportsCarVendor extends CarVendor<SportsCar<?>> {
@Override
<F extends FuelType> Sale<SportsCar<F>> sell(SportsCar<F> param);
}
SportsCarVendor
obviously doesn't compile since the signature sell(SportsCar<F>)
doesn't match with the expected type SportsCar<?>
.
Can anybody offer a viable solution for this problem?
Upvotes: 4
Views: 129
Reputation: 43186
If you really have to handle fuel type separately for each car vendor (maybe tax reasons?), here is how you define your car vendor to allow this:
interface CarVendor<F extends FuelType, C extends Car<F>> {
Sale<C> sell(C param);
}
interface SportsCarVendor <F extends FuelType> extends CarVendor<F, SportsCar<F>> { }
Now for the concrete implementations:
class SportsCarVendorDiesel implements SportsCarVendor<DieselFuel> {
@Override
public Sale<SportsCar<DieselFuel>> sell(SportsCar<DieselFuel> param) {
return null;
}
}
class SportsCarVendorGas implements SportsCarVendor<GasolineFuel> {
@Override
public Sale<SportsCar<GasolineFuel>> sell(SportsCar<GasolineFuel> param) {
return null;
}
}
The rest of the classes I used are as so:
interface FuelType {
double burnRate();
}
class DieselFuel implements FuelType {
@Override
public double burnRate() {
return 0;
}
}
class GasolineFuel implements FuelType {
@Override
public double burnRate() {
return 0;
}
}
interface Car<F extends FuelType> { }
interface SportsCar<F extends FuelType> extends Car<F> { }
class Sale<C> { }
Upvotes: 0
Reputation: 178293
From what you have provided so far, I don't see any reason why most of your interfaces and classes should be generic, and I see reasons why most should not be generic.
FuelType
sounds like it should be an attribute, not a type parameter, of a Car
. Perhaps it could be declared as an enum, depending on your exact requirements.
enum FuelType {
PETROL,
DIESEL;
}
public class Car {
private FuelType fuelType;
// rest of implementation
}
Similarly, Car
should be an attribute, not a type parameter, of a Sale
.
public class Sale {
private Car sold;
// rest of implementation
}
You may still need SportsCarVendor
to be generic so that you can narrow the type of car
the implementation class can sell, but the Sale
the sell
method returns still doesn't need to be generic.
interface CarVendor<C extends Car> {
Sale sell(C car);
}
interface SportsCarVendor extends CarVendor<SportsCar> {
@Override
Sale sell(SportsCar car);
}
Additionally, if you happen to need a specific subclass of Sale
, e.g. SportsCarSale
, then you can use return-type covariance, which is the ability for a subclass to narrow the return type without generics:
interface SportsCarVendor extends CarVendor<SportsCar> {
@Override
SportsCarSale sell(SportsCar car);
}
Upvotes: 3