使用“extends”和“super”通配符的泛型限定符

10

我正在开发一个项目,需要将服务添加到组件中。 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需要实现(扩展)tasktask扩展了Service接口,就像这样(不能编译)

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

1
这样怎么样:<T extends Service, S extends T> void addService(S service, Class<T> clazz); - Andy Turner
2个回答

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,因为我没有定义那些内容进行编译。


可以,非常感谢!我遇到的唯一问题是varargs强制所有数组元素都是相同类型,因此我无法编写addService(null, CarWash.class, CarRepair.class); - Frithjof
好的,现在你没有问有关可变参数(varargs)的事情...那听起来像是一种情况,你本来就不应该使用数组,而应该优先使用迭代器(Iterable),例如 static <T extends Service, S extends T> void addService(S service, Iterable<? extends Class<? extends T>> clazzes)。但我认为这样做对于service实现所有这些类可能不会产生正确的结果。在那种情况下,你最好定义明确的重载,有1个、2个、3个等类参数。听起来很乱。 - Andy Turner
我同意。我认为将其作为一个参数并多次调用该方法以添加多个任务的服务是可以的,就像您在答案中所做的那样。 - Frithjof

1
这也可能是可以的,取决于您如何使用service类型:
<T extends Service> void addService(T service, Class<T> task)

如果service是由task所代表的类型的子类型,则它总是可以进行向上转型。
addService(new Garage(), CarWash.class); // OK, Garage is a CarWash

我唯一遇到的问题是可变参数要求所有数组元素都是相同类型,因此我无法编写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有一种叫做交集类型(如果我们有一个类型<? extends T & U>,那么T & U部分被称为交集类型),但交集类型不能结合superextends,而且它们在其他方面也非常有限。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接