3

I bumped recently (twice) at following problem - is it possible to find on classpath specific implementation of generic interface? To visualize this please consider these snippets of code:

public interface Cleaner<T extends Room> {}
public class KitchenCleaner implements Cleaner<Kitchen> {}
public class BathroomCleaner implements Cleaner<Bathroom> {}

Now - Is it possible to have a following method ?:

public static <T extends Room> findCleanerServiceForRoomType(T room) {
    return ???
}

returning KitchenCleaner for Kitchen class and BathroomCleaner for Bathroom class? Of course I would like to have it extensible so that when new Room and Service type is added this method still works... So no switches or ifs :)

Piotr Tempes
  • 1,091
  • 1
  • 12
  • 26
  • If they’re your classes, you can add a getType() method to the interface and you can include an SPI descriptor in your .jar file(s) and use [java.util.ServiceLoader](http://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html). – VGR Feb 17 '17 at 15:26
  • The main problem is to find all classes on classpath. Java does not have built-in support for that. But you can do that yourself, by enumerating all classes on classpath; or find some library that does that. And then, for each class, you can use reflection to see whether it implements `Cleaner` of some specific `T` – ZhongYu Feb 17 '17 at 15:29
  • As so often the case, so-called duplicate isn't the same question and prevents useful answers from being posted. My advice: no automatic support for this, so create a CleanerRegistry wrapping a `Map` (or maybe something like `Map, Class>`). – Mark Adelsberger Feb 17 '17 at 15:32

3 Answers3

1

You can add a method to your Room definition (interface or abstract class) which provides an appropriate cleaner for you. Since you need to add a Cleaner when you add a room, there's no additional configuration.

public interface Room {
  Cleaner<? extends Room> cleanerInstance();
}

public class Bathroom implements Room {
  public Cleaner<Bathroom> cleanerInstance(){
    return new BathroomCleaner();
  }
}

If you want to find it without the Room being aware, you'll need some sort sort of configuration or lookup.

WillD
  • 875
  • 1
  • 7
  • 14
  • So obvious. Thanks :) I am gonna accept this answer since it solves one of cases. The other one can't be solved by this solution, though since Room class is not my class, it comes from library (and it is enum actually)... But I can live with switch statement there because it is in factory. Thanks! – Piotr Tempes Feb 17 '17 at 15:48
  • You're welcome, good luck! – WillD Feb 17 '17 at 15:50
  • The one issue with this code is that the type of the Cleaner isn't tied to the Room type, except by the explicit creation. It is still possible to use an inappropriate Cleaner on a Room (as cleanerInstance() is types as extends Room, not the specific class) You can declare Room as `Room` which allows you to do `public class Bathroom implements Room` and `Cleaner cleanerInstance();` to more explicitly tie them together. – WillD Feb 17 '17 at 16:09
0

Since generics do not exist beyond compilation, the answer to this question is no

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • I know generic arguments are erased, but that does not answer my question at all :) Below is actually quite good answer :) – Piotr Tempes Feb 17 '17 at 15:43
-1

Since Generics erase type at runtime I'd advise you to put a generic method in your interface returning the cleaned room :

public Class<? extends Room> getCleanedClass(){
    //override in every implementation
    return Kitchen.class;
}

And then you can use a predicate on this method.

Jeremy Grand
  • 2,300
  • 12
  • 18
  • I am not sure how could this solve the problem. – Piotr Tempes Feb 17 '17 at 15:44
  • Well, I assumed that you could already scan the whole classPath to get every class and you just needed to overcome type erasure to perfome a check at class level. But glad to see you found your solution. – Jeremy Grand Feb 17 '17 at 15:51