jeudi 17 octobre 2019

Iterating through a wildcard list and calling a method on it's elements (Is casting bad design?)

I have a List that uses a wildcard as follows :

List<DataLink<? extends Context>> dataLinks = new ArrayList<>();
dataLinks.add(new ContextDataLink());
dataLinks.add(new SomeOtherContextDataLink());

I want to be able to iterate through this List and call the retrieve method on each element :

Context context = new SomeOtherContext();

for(DataLink<? extends Context> dataLink : dataLinks) {
   System.out.println(dataLink.retrieve(context));
}

However, I get the following compiler error in the println statement :

The method retrieve(capture#3-of ? extends Context) in the type >DataLink<capture#3-of ? extends Context> is not applicable for the arguments >(Context)

Is there a way to achieve the above objective? It doesn't necessarily have to use generics but I feel that using generics would allow me to prevent explicit casting

I went through some answers on SO and understand why this error occurs. However, I am unable to find a solution to resolve it. I understand that I can write a method to capture the wildcard but this doesn't seem to resolve the problem as well :

 public static <T extends Context> void retrieve(List<DataLink<T>> 
       dataLinks,T context) {
      for(DataLink<T> dataLink : dataLinks) {
          dataLink.retrieve(context);
      }
 }

When I call the above retrieve method, I get the following compilation error :

The method retrieve(List<DataLink<T>>, T) in the type MyClass is not >applicable for the arguments >(List<DataLink<? extends Context>>, Context)

Following is the defintion of the Context, SomeOtherContext, DataLink implementations :

 static class Context {
     public String doContextThings() {
    return "contextThings";
      }
  }

static class SomeOtherContext extends Context {
    public String doSomeOtherContextThings() {
        return "someOtherContextThings";
    }
  }

interface DataLink<T extends Context> {
    public String retrieve(T context);
}

static class ContextDataLink implements DataLink<Context> {

    @Override
    public String retrieve(Context context) {
        return context.doContextThings();
    }

}

static class SomeOtherContextDataLink implements 
   DataLink<SomeOtherContext> {

    @Override
    public String retrieve(SomeOtherContext context) {
        return context.doSomeOtherContextThings();
    }

}

Note : A solution that doesn't involve generics is the most straightforward solution where the DataLink class is modified to not take a type parameter and always take a Context as the parameter type for the retrieve method. Here, subclasses would cast the Context to whatever type they are expecting it to be. However, the question remains the same Is this considered bad design or is casting acceptable in such cases?

Aucun commentaire:

Enregistrer un commentaire