【问题标题】:Covariance on a generic class with generic methods具有泛型方法的泛型类的协方差
【发布时间】:2018-08-28 12:32:58
【问题描述】:

在泛型类上使用协方差时,我无法正确定义泛型方法,如果这完全符合我的意图的话。我最好通过例子来解释手头的问题。

假设我们有以下汽车接口设置

interface Car { ... }
interface SportsCar extends Car { ... }

以及用于汽车供应商的此类通用接口返回 Sale 对象

interface CarVendor<C extends Car> {    
  Sale<C> sell(C car);
}

interface SportsCarVendor extends CarVendor<SportsCar> {    
  @Override
  Sale<SportsCar> sell(SportsCar car);
}

现在假设我们希望我们的汽车是通用的,例如关于燃料类型:

interface Car<F extends FuelType> { ... }
interface SportsCar<F extends FuelType> extends Car<F> { ... }
class PetrolSportsCar extends SportsCar<Petrol> { ... }
class DieselSportsCar extends SportsCar<Diesel> { ... }

如果我们不希望他们能够以任何种类的燃料销售汽车,我们在重新定义供应商界面时就会遇到问题。一个泛型方法似乎是答案,但是我无法正确定义它,因为泛型 Car&lt;?&gt; 是在类上定义的,但泛型 FuelType 应该在方法上定义。要了解这个想法:

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 显然无法编译,因为签名 sell(SportsCar&lt;F&gt;) 与预期的类型 SportsCar&lt;?&gt; 不匹配。

谁能为这个问题提供一个可行的解决方案?

【问题讨论】:

  • 如果燃料是汽车的固有属性,汽车厂商只销售一种类型的汽车,那么汽车厂商只销售一种类型的汽车和一种燃料。要么将燃料移动到供应商界面;要么或将汽车类型移到方法上。

标签: java generics inheritance covariance


【解决方案1】:

从您目前提供的内容来看,我看不出为什么您的大多数接口和类应该是泛型的,而且我看到大多数不应该是泛型的原因。

FuelType 听起来应该是Car属性,而不是类型参数。也许可以将其声明为枚举,具体取决于您的具体要求。

enum FuelType {
    PETROL,
    DIESEL;
}

public class Car {
    private FuelType fuelType;
    // rest of implementation
}

同样,Car 应该是 Sale 的属性,而不是类型参数。

public class Sale {
    private Car sold;
    // rest of implementation
}

您可能仍然需要SportsCarVendor 是通用的,以便您可以缩小实现类可以销售的car 的类型,但是Sale 方法返回的sell 仍然不需要是通用的。

interface CarVendor<C extends Car> {    
    Sale sell(C car);
}

interface SportsCarVendor extends CarVendor<SportsCar> {    
    @Override
    Sale sell(SportsCar car);
}

此外,如果您碰巧需要 Sale 的特定子类,例如SportsCarSale,那么就可以使用return-type covariance,这是子类在没有泛型的情况下缩小返回类型的能力:

interface SportsCarVendor extends CarVendor<SportsCar> {    
    @Override
    SportsCarSale sell(SportsCar car);
}

【讨论】:

    【解决方案2】:

    如果您确实必须为每个汽车供应商单独处理燃料类型(可能是税收原因?),您可以通过以下方式定义汽车供应商以允许这样做:

    interface CarVendor<F extends FuelType, C extends Car<F>> {
        Sale<C> sell(C param);
    }
    
    interface SportsCarVendor <F extends FuelType> extends CarVendor<F, SportsCar<F>> { }
    

    现在是具体实现:

    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;
        }
    }
    

    我使用的其余类是这样的:

    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> { }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-29
      • 1970-01-01
      • 1970-01-01
      • 2020-11-20
      • 2011-02-09
      • 1970-01-01
      相关资源
      最近更新 更多