Generics with extension and super

I am working on a project in which services should be added to the component. The Service class is an interface without any methods. Here is an example of how my services work:

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

Now there are many implementations of these services. One class can implement several services, since this class of garage:

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

When adding a service to a component, I don’t want to use the service for all tasks, but, for example, use Garage only to wash cars ( CarWash ), but not to restore them ( CarRepair ). Therefore, I define tasks as classes, like this:

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

To check if the service could actually complete the task, I used generics:

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

This works well, but does not check if the provided task is actually a task (a class that implements Service ), so this will work:

 addService(myTask, Object.class); 

I am looking for a way to indicate that Service needs to implement (extend) the task And task extends the Service interface , like this (not compilation):

 <T extends Service> addService(T service, Class<? super T extends Service> task); 
+8
java generics wildcard
source share
2 answers

I think that <T extends Service, S extends T> void addService(S service, Class<T> clazz) sounds as if it meets your criteria:

 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 added some statics and removed Car from the method signatures since I don't have a compilation definition)

+7
source share

This may also be good, depending on how you use the service type:

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

If service is a subtype of the type represented by task , then it can always be promoted.

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

The only problem I ran into is varargs, which cause all elements of the array to have the same type, and therefore I cannot write addService(null, CarWash.class, CarRepair.class);

This is a really tricky issue for Java generics. (C ++ can do this using variadic templates , which is a feature of Java, is unlikely to succeed.)

Thus, one way to solve this problem in Java is to check the runtime, for example:

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

(Or use Class<? extends Service> and make sure task.isInstance(service) .)

But I know that we don’t like it very much .; )

In Java, there is something called the intersection type (where, if we have the type <? extends T & U> , the T & U is called the intersection type), but the intersection type cannot combine super and extends , and they otherwise pretty limited in what else they can do.

+1
source share