泛型通配符“扩展”和“超级”

时间:2015-04-04 21:23:08

标签: java generics wildcard

我正在开发一个需要将服务添加到组件的项目。 Service类是没有任何方法的接口。以下是我的服务如何运作的示例:

public interface Service { }

public interface CarWash extends Service {
  void washCar(Car car);
}

public interface CarRepair extends Service {
  void repairCar(Car car);
}

现在有很多这些服务的实现。单个类可以实现多个服务,如此车库类:

public class Garage implements CarWash, CarRepair {
  @Override
  public void washCar(Car car) { /* .. */ }
  @Override
  public void repairCar(Car car) { /* .. */ }
}

在向组件添加服务时,我不希望将该服务用于所有任务,但是例如仅使用Garage来清洗汽车(CarWash)但不能用于修复他们(CarRepair)。因此,我将任务指定为类,如下所示:

void addService(Service service, Class<? extends Service> task);

要检查服务是否可以实际执行任务,我使用了泛型:

<T extends Service> addService(T service, Class<? super T> task);

这很好但不检查提供的任务是否实际上是一个任务(一个实现Service的类),所以这可以工作:

addService(myTask, Object.class);

我正在寻找一种方法来指定service需要实现(扩展)task并且task正在扩展Service接口,< / strong>喜欢这个(不编译)

<T extends Service> addService(T service, Class<? super T extends Service> task);

2 个答案:

答案 0 :(得分:7)

我认为<T extends Service, S extends T> void addService(S service, Class<T> clazz)听起来符合您的标准:

public static class Foo {                                                                                   
  public interface Service { }                                                                          

  public interface CarWash extends Service {                                                            
    void washCar();                                                                                     
  }                                                                                                     

  public interface CarRepair extends Service {                                                          
    void repairCar();                                                                                   
  }                                                                                                     

  static <T extends Service, S extends T> void addService(S service, Class<T> clazz) {}                 

  public static void main(String[] args) {                                                              
    addService(null, CarWash.class);  // Fine.                                                                  
    addService(null, Object.class);   // Compilation error.                                                          
  }
}

(我添加了一些静态并从方法签名中删除了Car,因为我没有定义要编译的那些)

答案 1 :(得分:1)

这也可能没问题,具体取决于您使用service的类型:

<T extends Service> void addService(T service, Class<T> task)

如果servicetask所代表类型的子类型,则可以随时对其进行上传。

addService(new Garage(), CarWash.class); // OK, Garage is a CarWash

  

我遇到的唯一问题是强制所有数组元素属于同一类型的varargs,因此我无法编写addService(null, CarWash.class, CarRepair.class);

这实际上是Java泛型的一个难题。 (C ++可以使用variadic templates执行此操作,这是Java不太可能获得的功能。)

因此,在Java中解决此问题的一种方法是使用运行时验证,例如:

<T extends Service> void addService(
        T service, Class<? super T> tasks...) {
    for(Class<? super T> task : tasks)
        if(!Service.class.isAssignableFrom(tasks))
            throw new IllegalArgumentException();
}

(或使用Class<? extends Service>并检查task.isInstance(service)。)

但我知道我们并不喜欢这样。 ; )

Java确实有一个名为intersection type的东西(如果我们有<? extends T & U>类型,T & U部分称为交集类型),但交集类型不能组合{{1} }}和super他们在其他方面做得非常有限。