简体   繁体   中英

Generic Type in java: How to define a function with multiple different type returned

@Service
public class Animal {
   public String name;
}
@Service
public class Dog extends Animal {
   public String name;
}

@Service
public class Cat extends Animal {
   public String name;
}

In spring boot project, I wanna obtain one specific bean by using ApplicationContext provided by spring framework, here is a simple example i write to illustrate:

@Component
public class AnimalLocator implements ApplicationContextAware {
   private static ApplicationContext applicationContext;
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      if (PayServiceLocator.applicationContext == null) {
         PayServiceLocator.applicationContext = applicationContext;
      }
   }

   public <T extends Animal> T getService(String name) {
      if("Cat".equals(name) {
        applicationContext.getBean(name, Cat.class);
      }
      if("Dog".equals(name) {
        applicationContext.getBean(name, Dog.class);
      }
   }
}

However, exceptions was prompted by the compiler:

在此处输入图片说明

the mosaic part should be Dog or cat. I thought it should work, since T has already extended the Animal class, but it doesn't ,so does anyone has any ideas about it? Thank you!

Since you're using bean class to access bean instance, it's straight forward to pass class as parameter:

public <T extends Animal> T getPayService(String name, Class<T> payClass) {
   return applicationContext.getBean(name, payClass);
}

T in getPayService will extend Animal , of course. This means that code calling it with another type won't compile:

Fruit fruit = animalLocator.getPayService("Banana")

To illustrate your current problem , look at this:

Cat cat = animalLocator.getPayService("Dog");

T is Cat in this case, but your code would be returning a Dog .

To circumvent the compiler error, you can add a type cast:

return (T) applicationContext.getBean(...

But this would still not be safe because the compiler will still be unable to guarantee that the actual return type will be what T is in the context of the caller at runtime, and the caller would be having a class cast exception.

If we can assume that getBean is a safe call, then you should change your method to this implementation:

public <T extends Animal> T getPayService(String name, Class<T> cls) {
   return applicationContext.getBean(name, cls);
}

This doesn't change a lot from the caller's perspective, but hinges on the fact (or assumption) that applicationContext.getBean(name, cls); will return an object of type T . This means your code is as type-safe as getBean is, but the compiler is happy with that.

You can Autowired all your Animal instances in a Map instead of coding your if/else :

@Service("Animal")
public class Animal {
    public String name;
}
@Service("Dog")
    public class Dog extends Animal {
}
@Service("Cat")
    public class Cat extends Animal {
}

And in your AnimalLocator :

@Component
public class AnimalLocator {

   @Autowired
   private Map<String,Animal> animals;

   public <T extends Animal> T getService(String name) {
      return this.animals.get(name);
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM