Список <Список > проблема с дженериками

#java #list #generics

#java #Список #дженерики

Вопрос:

У меня есть следующий метод:

 public static List<List<String>> createObject() {
    List<List<String>> listOfListOfStrings = new LinkedList<List<String>>();
    List<String> listOfStrings = new LinkedList<String>();
    //do some populating on the lists here
    listOfListOfStrings.add(listOfStrings);
    return listOfListOfStrings;
}
  

Теперь я хочу иметь возможность использовать ArrayList / Vector / Stack вместо LinkedList (и при необходимости использовать разные комбинации. Я прочитал несколько сообщений о дженериках, касающихся этой проблемы, и я обнаружил, что использование заводского шаблона для их создания наиболее подходит (поскольку я не хочу отражения, и поскольку использование универсального <T extends List<K>, K extends List<String>> не будет работать.). Итак, решение, которое я придумал, заключается в следующем:

 public class Tester {
public static void main(String[] args){
    checkGenericsOfLists();
}

public static void checkGenericsOfLists(){
    List<List<String>> listOfListOfStrings = createObject(new LinkedListFactory<List<String>>(), new LinkedListFactory<String>());
    print(listOfListOfStrings);
    translate(listOfListOfStrings);
    print(listOfListOfStrings);
}

public static List<List<String>> createObject(ListFactorable mainListFactory, ListFactorable subListsFactory) {
    List<List<String>> listOfListOfStrings = mainListFactory.create();//new LinkedList<List<String>>();
    List<String> listOfStrings = subListsFactory.create();//new LinkedList<String>();
    listOfStrings.add("A");
    listOfListOfStrings.add(listOfStrings);
    return listOfListOfStrings;
}

public static void transform(List<List<String>> listOfListOfStrings){ 
            //do some abuse on the lists here.
}}
  

Это решение выдает предупреждения:

ListFactorable — это необработанный тип. Ссылки на общий тип ListFactorable должны быть параметризованы

Это для сигнатуры метода CreateObject. И:

Безопасность типов: выражение списка типов требует непроверенного преобразования, чтобы соответствовать списку списка строк>>

Это для строк, в которых фабрика вызывает метод create() .

Но если я использую этот:

 public static List<List<String>> createObject(ListFactorable<List<List<String>>, List<String>> mainListFactory, ListFactorable<List<String>, String> subListsFactory) {
    List<List<String>> listOfListOfStrings = mainListFactory.create();//new LinkedList<List<String>>();
    List<String> list = subListsFactory.create();//new LinkedList<String>();
    list.add("A");
    listOfListOfStrings.add(list);
    return listOfListOfStrings;
}
  

Я получаю ошибку компилятора:

 The method createObject(ListFactorable<List<List<String>>,List<String>>, ListFactorable<List<String>,String>) in the type Tester is not applicable for the arguments (LinkedListFactory<List<String>>, LinkedListFactory<String>)
  

Есть ли какой-либо способ заставить компилятор не выдавать предупреждение или ошибку и создавать экземпляры этих списков без того, чтобы метод CreateObject знал об используемой реализации списка (во время компиляции) ?!

Приветствую,
деспот

РЕДАКТИРОВАТЬ: Извините, что я не публикую остальные классы (очень глупо с моей стороны:)). Поехали:

 public interface ListFactorable<T extends List<K>, K> {
T create();}

public class LinkedListFactory<K> implements ListFactorable<LinkedList<K>, K> {
public LinkedList<K> create(){
    return new LinkedList<K>();
}}
  

EDIT2 (Евгений берет на себя проблему):

 public interface EugeneListFactorable<T extends List<?>> {T create();}

public class EugeneLinkedListFactory implements EugeneListFactorable<LinkedList<?>> {
    public LinkedList<?> create(){
        return new LinkedList<List<?>>();
    }
}
public static void checkGenericsOfLists2(){
    List<List<String>> listOfListOfStrings = createObject(new EugeneLinkedListFactory(), new EugeneLinkedListFactory());
    translate(listOfListOfStrings);
}
  

Ошибка компилятора:

     - Type mismatch: cannot convert from LinkedList<?> to List<List<String>>
- Bound mismatch: The generic method createObject(EugeneListFactorable<N>, EugeneListFactorable<M>) of type 
 Tester is not applicable for the arguments (EugeneLinkedListFactory, EugeneLinkedListFactory). The inferred type LinkedList<?> 
 is not a valid substitute for the bounded parameter <N extends List<List<String>>>
  

Пожалуйста, попробуйте скомпилировать и запустить опубликованный мной пример и ваше решение. Это тестовый класс, который вы можете легко запустить в вашей IDE. Спасибо!

Комментарии:

1. не могли бы вы опубликовать класс ListFactorable ? и, возможно, SSCCE .. И обратите внимание, что у первого параметра CreateObject нет имени.

2. Пожалуйста, опубликуйте определения LinkedListFactory и ListFactorable.

3. @Heisenbug «post ListFactorable» готово! У CreateObject есть имя первого параметра — прокрутите вправо (и внимательно посмотрите на дженерик). Что такое SSCCE ?!

4. @Andrey «опубликовать определения LinkedListFactory и ListFactorable» готово!

Ответ №1:

Следующий материал «работает на моей машине» без предупреждений или ошибок. То, что находится за пределами моего восприятия, является причиной всей этой суеты — см. Второй кодовый блок.

 public class Tester {

    public static void main(String[] args) {
        checkGenericsOfLists();
    }

    public static void checkGenericsOfLists() {
        List<List<String>> listOfListOfStrings = createObject(
                new LinkedListFactory<List<String>>(),
                new LinkedListFactory<String>());
        transform(listOfListOfStrings);
    }

    public static List<List<String>> createObject(
            ListFactorable<List<List<String>>, List<String>> mainListFactory, 
            ListFactorable<List<String>, String> subListsFactory
    ) 
    {
        List<List<String>> listOfListOfStrings = mainListFactory.create();
        List<String> listOfStrings = subListsFactory.create();
        listOfStrings.add("A");
        listOfListOfStrings.add(listOfStrings);
        return listOfListOfStrings;
    }

    public static void transform(List<List<String>> listOfListOfStrings) {
        // do some abuse on the lists here.
        System.out.println(listOfListOfStrings);
    }

    public interface ListFactorable<T extends List<K>, K> {
        T create();
    }

    static public class LinkedListFactory<K> implements ListFactorable<List<K>, K> {
        public LinkedList<K> create() {
            return new LinkedList<K>();
        }
    }
}
  

Это решение немного чище, исключая некоторый шум дженериков.

 public class Tester2 {
    public static void main(String[] args) {
        checkGenericsOfLists();
    }

    public static void checkGenericsOfLists() {
        List<List<String>> listOfListOfStrings = createObject(
                new LinkedListFactory<List<String>>(),
                new LinkedListFactory<String>());
        transform(listOfListOfStrings);
    }

    public static List<List<String>> createObject(
            ListFactory<List<String>> mainListFactory, 
            ListFactory<String> subListsFactory
    ) 
    {
        List<List<String>> listOfListOfStrings = mainListFactory.create();
        List<String> listOfStrings = subListsFactory.create();
        listOfStrings.add("A");
        listOfListOfStrings.add(listOfStrings);
        return listOfListOfStrings;
    }

    public static void transform(List<List<String>> listOfListOfStrings) {
        // do some abuse on the lists here.
        System.out.println(listOfListOfStrings);
    }

    public interface ListFactory<T> {
        List<T> create();
    }

    static public class LinkedListFactory<T> implements ListFactory<T> {
        public List<T> create() {
            return new LinkedList<T>();
        }
    }
}
  

Комментарии:

1. Упрощение дженериков в заводском интерфейсе и ссылочный тип возврата интерфейса (плохой дизайн с моей стороны для возврата ссылочного типа реализации) отлично работают! Спасибо.

Ответ №2:

Это точно не решит вашу проблему, но поможет быстрее найти решение.

Когда у вас есть что-то похожее на это X> рассмотрите возможность рефакторинга в класс. Я приведу пример. Похоже, вы используете транзакции. Теперь я точно не знаю, каковы ваши цели, потому что вы их не указали, но одним из распространенных способов сделать это было бы:

 List<String> english = new LinkedList<String>();
List<String> french = new LinkedList<String>();
List<String> spanish = new LinkedList<String>();

List<List<String>> languages = new LinkedList<List<String>>();
languages.add(english);
languages.add(french);
languages.add(spanish);

for(String word : someWordList) {
    for(List<String> language : languages) {
         // oh snap! I really have no idea what language I'm working in...
         // I could do something silly like make list.get(0) be the language
         // name, but that seems lame
    }
}
  

Что было бы лучше, было бы что-то вроде этого:

 class Language {
    final String language;
    final Map<String,String> english2current;
    Language(String language, Set<String> words) {
        this.language = language;
        english2current = new HashMap<String,String>();
        Translator t = TranslatorFactory.getTranslator(language);
        for(String word : words) english2current.put(word,t.translate(word));
    }
}

Collection<String> wordSet() {
    return english2current.values();
}
  

Теперь, не видя больше вашей реализации, трудно понять, что именно вы делаете неправильно. Но ЧТО БЫ вы ни делали, посмотрите на удаление любых вложенных дженериков и вместо этого попытайтесь изолировать их в классы. Это позволит вам консолидировать поведение объектно-ориентированным образом и лучше организовать вашу логику, чтобы вы могли сосредоточиться на том, что она должна делать, а не на семантике дженериков.

Комментарии:

1. Допустим, я создаю класс listOfStrings, в котором есть переменная-член List<Строка>. В тот момент, когда мне нужно ввести что-то в список (в методе CreateObject), я снова столкнусь с проблемой создания, и в этот момент мне снова нужно будет создать экземпляр этого класса 1. либо путем предоставления реализации списка в конструкторе — вернуться к квадрату 1 или 2. или путем предоставленияуниверсальный в классе listOfStrings, который приведет меня к проблеме с дженериками. Кстати, мне понравилась ваша идея об использовании специального класса, но вы должны предоставить лучшее решение независимо от реализации — посмотрите на вопрос.

Ответ №3:

Вам нужно правильно указать класс ListFactorable:

 public class ListFactorable<T extends List<?>> {
  public T create() {
    ...
  }
}
  

Тогда метод CreateObject может выглядеть примерно так:

 public static <N extends List<List<String>>,M extends List<String>> N createObject( //
        ListFactorable<N> mainListFactory, //
        ListFactorable<M> subListsFactory) {
    N listOfListOfStrings = mainListFactory.create();//new LinkedList<List<String>>();
    M listOfStrings = subListsFactory.create();//new LinkedList<String>();
    listOfStrings.add("A");
    listOfListOfStrings.add(listOfStrings);
    return listOfListOfStrings;
}
  

Комментарии:

1. Я написал правку с предложением, которое вы мне дали. К сожалению, я не смог найти работоспособное решение. Пожалуйста, проверьте это и попробуйте запустить тестовую программу с любым решением, которое у вас есть. Спасибо!