2015-04-04 80 views
8

我正在开发一个需要将服务添加到组件的项目。 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); 

我正在寻找一种方法符合规范IFY是service需要实现(延长)的tasktask是扩展Service接口,这样(不编译)

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

如何:' void addService(S服务,类 clazz);'? – 2015-04-04 21:39:12

回答

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

(I增加了一些静力学和从方法签名除去Car,因为我没有这些的定义来编译)

+0

作品,非常感谢!我遇到的唯一问题是强制所有数组元素具有相同类型的可变参数,因此我无法编写'addService(null,CarWash.class,CarRepair.class);'。 – Frithjof 2015-04-04 21:50:05

+0

那么现在你没有询问可变参数......这听起来像是你并不真正想要使用数组,而应该更喜欢使用Iterable, 'static void addService(S service,Iterable <?extends Class > clazzes)''。但我认为这对于执行所有这些类的“服务”来说并不合适。我认为在这种情况下,您实际上可以更好地定义带有1,2,3等类参数的显式过载。听起来很乱。 – 2015-04-04 21:53:43

+0

我同意。我认为把它作为一个参数并且多次调用该方法来为多个任务添加服务是很好的,就像你在答案中一样。 – Frithjof 2015-04-04 21:58:58

1

这也可能是罚款,这取决于你如何使用的service类型:

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

如果servicetask代表的类型的子类型,那么它总是可以upcasted。

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确实有一些所谓的intersection type(其中,如果我们有一个类型<? extends T & U>,该T & U部分被称为交集型),但交集型不能结合superextends和他们另有相当的还有什么限制他们可以做。