#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. Я написал правку с предложением, которое вы мне дали. К сожалению, я не смог найти работоспособное решение. Пожалуйста, проверьте это и попробуйте запустить тестовую программу с любым решением, которое у вас есть. Спасибо!