Итерация по списку подстановочных знаков и вызов метода для его элементов (Приведение в заблуждение?) - PullRequest
0 голосов
/ 17 октября 2019

У меня есть List, который использует подстановочный знак следующим образом:

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

Я хочу иметь возможность перебирать этот List и вызывать метод retrieve для каждого элемента:

Context context = new SomeOtherContext();

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

Однако в операторе println я получаю следующую ошибку компилятора:

Метод retrieve(capture#3-of ? extends Context) в типе >DataLink<capture#3-of ? extends Context> не применим для аргументов> (Context)

Есть ли способ достичь вышеуказанной цели? Не обязательно использовать дженерики, но я чувствую, что использование дженериков позволило бы мне предотвратить явное приведение

Я прошел некоторые ответы на SO и понимаю, почему возникает эта ошибка. Однако я не могу найти решение, чтобы решить его. Я понимаю, что могу написать метод для захвата подстановочного знака, но, похоже, это тоже не решает проблему:

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

Когда я вызываю вышеуказанный метод получения, я получаю следующую ошибку компиляции:

Метод retrieve(List<DataLink<T>>, T) в типе MyClass> не> применим для аргументов> (List<DataLink<? extends Context>>, Context)

Ниже приводится определение Context, SomeOtherContext, DataLink реализации:

 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();
    }

}

Примечание. Решение, которое не включает в себя дженерики, является наиболее простым решением, когда класс DataLink модифицируется так, чтобы не принимать параметр типа и всегда принимать * 1042. * как тип параметра для метода retrieve. Здесь подклассы приведут Context к тому типу, который они ожидают. Тем не менее, вопрос остается прежним Это считается плохим дизайном или приемлемо литье в таких случаях?

Ответы [ 2 ]

2 голосов
/ 17 октября 2019

Основная проблема в том, что вы хотите дать методу retrieve аргумент, который он не поддерживает. Например, одним из элементов в вашем списке может быть DataLink<ContextA>, и вы хотите вызвать для него retrieve(ContextB).

Вам нужно написать некоторый код, который определяет, как это обрабатывается.

Если вы поместите этот код в реализации метода retrieve, то вы можете передать любой тип контекста, который вы хотите, любому методу retrieve, и вы можете объявить, что он принимает простой параметр Context. Затем каждая реализация может выполнить instanceof проверку контекста, который вы передаете, и решить, что с ним делать.

Параметр типа в классе DataLink не будет необходим для определения метода retrieve, но вы все равно можете включить его в класс и использовать его для других целей, если хотите.

0 голосов
/ 17 октября 2019

Список > dataLinks такие объявления разрешены только для операций чтения. Добавление элемента ограничено, так как базовым элементом может быть что угодно. Для вашего случая использования шаблон ниже может помочь

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

static class SomeOtherContext extends Context {
    public String doContextThings() {
        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 void  main(String[] a){
    List<DataLink<Context>> dataLinks = new ArrayList<>();
    dataLinks.add(new ContextDataLink());


    SomeOtherContext context = new SomeOtherContext();


    for(DataLink<Context> dataLink : dataLinks) {
        dataLink.retrieve(context);
    }
}
...