6

I have a generic functional interface:

@FunctionalInterface
public interface Feeder<T extends Animal> {
  void feed(T t);
}

And a couple of beans implementing that interface for different Animal subclasses.

@Configuration
public class Config {
  @Bean
  public Feeder<Dog> dogFeeder() {
    return dog -> dogService.feedDog(dog);
  }
  @Bean
  public Feeder<Cat> catFeeder() {
    return cat -> catService.feedCat(cat);
  }
}

Now a service class has been injected with these beans and is given an instance of Animal. How can it determine the correct Feeder bean to use?

@Service
public class PetStore {
  @Autowired
  private List<Feeder<? extends Animal> feeders;

  private void feed(Animal animal) {
    //TODO: How to determine the correct feeder from feeders?
    Feeder<? extends Animal> correctFeeder = ....
    correctFeeder.feed(animal);
  }
}

Things I've tried:

I initially thought I'd be alright using How to get a class instance of generics type T but am running into issues that the bean is implemented using a lambda function and the type returned when I call GenericTypeResolver.resolveTypeArgument(feeder.getClass(), Feeder.class) is Animal(!)

I then tried to use an anonymous subclass for the beans. Then GenericTypeResolver can determine the specific type of Animal each Feeder will feed. But IntelliJ is screaming at me I should create a lambda for it and so will other people using the PetStore.

I added a getAnimalClass() method to the Feeder interface. IntelliJ stops screaming. It does feel very clumsy though.

The first time I get an Animal instance of a class I've not yet fed, I try/catch to use each candidate feeder, until I find one that works. Then I remember the result for future use. Also feels very clumsy.

So my question: What is the proper way to do this?

Community
  • 1
  • 1
flup
  • 26,937
  • 7
  • 52
  • 74
  • I know I'm not necessarily being helpful, but issues like this only confirm my decision to inject dependencies manually. Roughly the same amount of text needed, easy to follow what happens and issues like this are a nobrainer: just create several objects and inject them where needed, no matter what their types. – Ville Oikarinen May 16 '17 at 07:41
  • @VilleOikarinen The reason why I inject the list using Spring is that this is library code and I don't know in advance what types of Animal and corresponding Feeder will be created by users of the library. – flup May 16 '17 at 13:29
  • Choosing types dynamically can be done easily by reflection (`Class.forName`), that's how Spring also does it, most probably. – Ville Oikarinen May 17 '17 at 06:20

2 Answers2

3

Short answer

I'm afraid there is no really clean way. Since the types are erased, you need to keep the type info somewhere and your third suggestion using the getAnimalClass() is one way to do it (however it's unclear in your question how you put it to use later on).

I would personally get rid of the lambda's and add a canFeed(animal) to the Feeder to delegate the decision (instead of adding the getAnimalClass()). That way, the Feeder is responsible for knowing what animals it can feed.

So the type info will be kept in the Feeder class, for example using a Class instance passed with construction (or by overriding a getAnimalClass() as you presumably did):

final Class<T> typeParameterClass;

public Feeder(Class<T> typeParameterClass){
    typeParameterClass = typeParameterClass;
}

So that it can be used by the canFeed method:

public boolean canFeed(Animal animal) {
   return typeParameterClass.isAssignableFrom(animal.getClass());
}

This will keep the code in the PetStore pretty clean:

private Feeder feederFor(Animal animal) {
    return feeders.stream()
                  .filter(feeder -> feeder.canFeed(animal))
                  .findFirst()
                  .orElse((Feeder) unknownAnimal -> {});
}

Alternative Answer

As you said, storing the type info makes it clumsy. You could however pass the type info in another, less strict way, without cluttering the Feeders, for example by relying on the names of the Spring beans you inject.

Let's say you inject all the Feeders in the PetStore in a Map:

@Autowired
Map<String, Feeder<? extends Animal>> feederMap;

Now you have a map that contains the feeder name (and implicit it's type) and the corresponding Feeder. The canFeed method is now simply checking the substring:

private boolean canFeed(String feederName, Animal animal) {
    return feederName.contains(animal.getClass().getTypeName());
}

And you can use it to fetch the correct Feeder from the map:

private Feeder feederFor(Animal animal) {
    return feederMap.entrySet().stream()
                    .filter(entry -> canFeed(entry.getKey(), animal))
                    .map(Map.Entry::getValue)
                    .findFirst()
                    .orElse((Feeder) unknownAnimal -> {});
}

Deep dive

The lambda's are clean and concise, so what if we want to keep the lambda expressions in the config. We need the type information, so the first try can be to add the canFeed method as a default method on the Feeder<T> interface:

default <A extends Animal> boolean canFeed(A animalToFeed) {
    return A == T;
}

Of course we can't do A == T, and because of type erasure, there is no way to compare the generic types A and T. Normally, there is the trick you referred to, that only works when using generic supertypes. You mentioned the Spring toolbox method, but let's look at the Java implementation:

this.entityBeanType = ((Class) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0]);

Since we have multiple Feeder implementations with a Feeder<T> superinterface, you might think we can adopt this strategy. However, we can't, because we are dealing with lambda's here and lambda's are not implemented as anonymous inner classes (they are not compiled to a class but use an invokedynamic instruction) and we loose the typing info.

So back to the drawing board, what if we make it an abstract class instead, and use it as a lambda. This however is not possible and Brian Goetz, chief language architect, explains why on the mailinglist:

Before throwing this use case under the bus, we did some corpus analysis to found how often abstract class SAMs are used compared to interface SAMs. We found that in that corpus, only 3% of the lambda candidate inner class instances had abstract classes as their target. And most of them were amenable to simple refactorings where you added a constructor/factory that accepted a lambda that was interface-targeted.

We could go and create factory methods to work around this, but that again would be clumsy and get us too far.

Since we got this far, let's take our lambda's back and try to fetch the type info in some other way. Spring's GenericTypeResolver did not bring the expected result Animal, but we could get hacky and take advantage of the way the Type info is stored in bytecode.

When compiling a lambda, the compiler inserts a dynamic invoke instruction that points to a LambdaMetafactory and a synthetic method with the body of the lambda. The method handle in the constant pool contains the generic type, since the generics in our Config our explicit. At runtime, ASM generates a class that implements the functional interface. Unfortunately, this specific generated class does not store the generic signatures and you can not use reflection to get around the erasure, since it is defined using Unsafe.defineAnonymousClass. There is a a hack to get the info out of the Class.getConstantPool that uses the ASM to parse and return the argument types, but this hack relies on undocumented methods and classes and is vulnerable to code changes in the JDK. You could hack it in yourself (by coping pasting the code from the reference) or use a library that implements this approach such as TypeTools. Other hacks could work too, such as adding Serialization support to the lambda and try to fetch the method signature of the instantiated interface from the serialized form. Unfortunately, I did not find a way yet to resolve the type info with the new Spring api's.

If we take this approach to add the default method in our interface, you can keep all your code (e.g. the config) and the actual Feeder-hack hack reduces to:

default <A extends Animal> boolean canFeed(A animalToFeed) {
    Class<?> feederType = TypeResolver.resolveRawArgument(Feeder.class, this.getClass());
    return feederType.isAssignableFrom(animalToFeed.getClass());
}

The PetStore stays clean:

@Autowired
private List<Feeder<? extends Animal>> feeders;

public void feed(Animal animal) {
    feederFor(animal).feed(animal);
}

private Feeder feederFor(Animal animal) {
    return feeders.stream()
                  .filter(feeder -> feeder.canFeed(animal))
                  .findFirst()
                  .orElse(unknownAnimal -> {});
}

So unfortunately, there is no straightforward approach and I guess we can safely conclude we checked all (or at least more than a few basic) options.

Community
  • 1
  • 1
Nick Vanderhoven
  • 3,018
  • 18
  • 27
0

There is no real 'clean' way to do this I would say. Questions surrounding auto-wiring components with type parameters have been dealt with before. But to claim your tasty bounty I will describe some methods you could employ. The proper way of doing this is something you would need to decide yourself. But because of the implementation of generics in Java as you have already said you can see that there is not really a nice way of doing this in a type safe manor. Whatever solution you come up with, you will have this issue of either not knowing 100% sure if a service has been given the correct type of animal to feed or the correct feeding service has been retrieved at compile time.

No matter the solution this will be your issue. The proper way I imagine most developers would accept is a solution whereby you are guaranteed you have correct Feeder and Animal at compile time, but you just can't, which just breaks my heart.

But then again I lie, because there certainly is a way to do this in a type safe manner in Java, but not really, that is, you could come up with something which would be type safe and guarantee the correct service and Animal but it would involve black magic, either with reflection, AspectJ, some nasty build plugin etc. no one would like it, especially not you who seeks this 'proper' solution.

To be honest I like the idea of a bunch of services each trying to feed your animal passing it on to the next service, sounds alot like a strategy pattern. Everyone would understand something like a Feeding strategy, if I saw that I would get what you were trying to do. When an Animal is given to it which it can't feed it would throw a NoFeedingServiceForThatAnimal or something. The alternative is to define a Feed lookup service for an animals, which would mean either an ID for each animal (enum maybe, would give you some type-safety), or use reflection but nobody likes reflection. IMHO strategy would be more versatile than a lookup type solution because you may have different feeding services for the same animal, for instance you might have a baby elephant feeder and an adult elephant feeder and it might make more sense to keep these separate rather than having just one elephant Feeder.

I'm aware I didn't give you code to see which appears to be the nicest, but it really is a matter for you to decide for your domain how you deal with these issues, and just identify some of the consequences these design decisions have. Good luck.

Trash Can
  • 6,608
  • 5
  • 24
  • 38
Derrops
  • 7,651
  • 5
  • 30
  • 60