green
green

Reputation: 393

How to handle generic interfaces implementation and avoid @SuppressWarnings?

I am trying to make a generic handling of different service implementations and I constantly receive the "Unchecked call due to raw type" error.

I have tried several implementations, but could not quite understand what is the issue here.

I have the following model:

public abstract class Fruit {

  public List<String> vitamins;
  public String originCountry;

  // getters and setters omitted
}

The concrete implementation are the following:

public class Kiwi extends Fruit {
}

public class Pineapple extends Fruit {
}

I have the following interface:

public interface FruitCheckService<T extends Fruit> {

  List<String> compareVitaminsFromDifferentCountries(T firstFruit, T secondFruit);

  Class<T> getImplementation();
}

With implementations for the 2 models given above:

@Service
public class KiwiCheckServiceImpl implements FruitCheckService<Kiwi> {

  @Override
  public List<String> compareVitaminsFromDifferentCountries(Kiwi firstFruit, Kiwi secondFruit) {
    // some implementation
    return new ArrayList<>();
  }

  @Override
  public Class<Kiwi> getImplementation() {
    return Kiwi.class;
  }
}

And pineapple:

@Service
public class PineappleCheckServiceImpl implements FruitCheckService<Pineapple> {

  @Override
  public List<String> compareVitaminsFromDifferentCountries(Pineapple firstFruit, Pineapple secondFruit) {
    // some implementation
    return new ArrayList<>();
  }

  @Override
  public Class<Pineapple> getImplementation() {
    return Pineapple.class;
  }
}

I have the following class which is manupulating with the different beans:

@Service
public class FruitServices {

  private Map<Class, FruitCheckService> beansMap;

  @Autowired
  public FruitServices(List<FruitCheckService> fruitCheckServices) {

    beansMap = new HashMap<>();

    fruitCheckServices
      .forEach(
        fruitCheckService -> {
          Class implementation = fruitCheckService.getImplementation();
          beansMap.put(implementation, fruitCheckService);
        }
      );
  }


  public FruitCheckService getFruitCheckService(Class clazz) {
    return beansMap.get(clazz);
  }
}

At the end, this is the service where I am calling this:

@Component
public class BusinessService {

  @Autowired
  private FruitServices fruitServices;

  public void compareVitamins(Fruit one, Fruit two) {

    Class<? extends Fruit> aClass = one.getClass();

    FruitCheckService fruitCheckService = fruitServices.getFruitCheckService(aClass);

    List<String> result = fruitCheckService.compareVitaminsFromDifferentCountries(one, two);
  }
}

How to use correctly the fruitCheckService without receiving "Unchecked call to 'compareVitaminsFromDifferentCountries(T, T)' as a member of raw type 'exercise2.service.FruitCheckService'"?

Upvotes: 1

Views: 101

Answers (1)

user11595728
user11595728

Reputation:

The problem is that you are using the raw type Class in your service definition. To get around this problem, you can use the bounded wildcard type ?, as such:

class FruitServices {

  private Map<Class<? extends Fruit>, FruitCheckService<? extends Fruit>> beansMap;


  public FruitServices(List<FruitCheckService<? extends Fruit>> fruitCheckServices) {

    beansMap = new HashMap<>();

    fruitCheckServices
      .forEach(
        fruitCheckService -> {
          Class<? extends Fruit> implementation = fruitCheckService.getImplementation();
          beansMap.put(implementation, fruitCheckService);
        }
      );
  }

  public FruitCheckService<? extends Fruit> getFruitCheckService(Class<? extends Fruit> clazz) {
    return beansMap.get(clazz);
  }
}

For class BusinessService things are going to be trickier. First, it is necessary to make compareVitamins a generic method to ensure both fruits are of the same type. But even then, I don't believe you can avoid an unchecked cast from the result of getFruitCheckService because, as FruitServices has to deal with services for different types of fruit, you can't have a precise type parameter for the return value of getFruitCheckService.

class BusinessService {

    private FruitServices fruitServices;

    public <T extends Fruit> void compareVitamins(T one, T two) {

        @SuppressWarnings("unchecked")
        FruitCheckService<T> fruitCheckService = 
            (FruitCheckService<T>) fruitServices.getFruitCheckService(one.getClass());

        List<String> result = fruitCheckService.compareVitaminsFromDifferentCountries(one, two);
    }
}

Upvotes: 1

Related Questions