Синтаксис Java Generics для массивов
В какой структуре данных указано следующее объявление?
List<ArrayList>[] myArray;
Я думаю, он должен объявить массив, где каждый элемент является List
(например, a LinkedList
или ArrayList
) и требует, чтобы каждый List
содержал объекты ArrayList
.
Мое рассуждение:
List<String> someList; // A List of String objects
List<ArrayList> someList; // A List of ArrayList objects
List<ArrayList>[] someListArray; // An array of List of ArrayList objects
После запуска некоторых тестов я решил, что он принимает массив, в котором каждый элемент является объектом LinkedList
и не указывает, какие объекты LinkedList содержат.
So List<ArrayList>
указывает, что должен содержать List
, но List<ArrayList>[]
указывает, как должен быть реализован List
.
Я что-то пропустил?
Вот мои тесты.
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
public class Generics1 {
public static void main(String[] args) {
List<ArrayList>[] someListArray;
someListArray = getArrayWhereEachElementIsAnArrayListObject();
// Why does this satisfy the declaration?
//someListArray[0] => ArrayList object holding Strings
someListArray= getArrayWhereEachElementIsAListOfArrayListObjects();
//someListArray[0] => ArrayList object holding ArrayList objects
}
public static List[] getArrayWhereEachElementIsAnArrayListObject() {
List[] arrayOfLists = new ArrayList[2];
arrayOfLists[0] = getStringList();
arrayOfLists[1] = getIntegerList();
return arrayOfLists;
}
public static List[] getArrayWhereEachElementIsAListOfArrayListObjects() {
List list1 = new ArrayList();
list1.add(getArrayList());
List list2 = new ArrayList();
list2.add(getArrayList());
List[] arrayOfListsOfArrayLists = new ArrayList[2];
arrayOfListsOfArrayLists[0] = list1;
arrayOfListsOfArrayLists[1] = list2;
return arrayOfListsOfArrayLists;
}
public static List getStringList() {
List stringList= new ArrayList();
stringList.add("one");
stringList.add("two");
return stringList;
}
public static List getIntegerList() {
List intList= new ArrayList();
intList.add(new Integer(1));
intList.add(new Integer(2));
return intList;
}
public static ArrayList getArrayList() {
ArrayList arrayList = new ArrayList() ;
return arrayList;
}
}
Ответы
Ответ 1
Ответ заключается в том, что массивы могут содержать только типы reified. И генерализованные классы не воссоединяются. То есть "тип" среды выполнения List <ArrayList> это просто список. Дженерики стираются во время выполнения (google "стена стирания" ).
Итак, это:
List<ArrayList>[] myArray
действительно означает:
List[] myArray
Нет безопасного типа для объявления того, что вы пытаетесь объявить. В общем случае я бы рекомендовал использовать List вместо массива в этом случае. Некоторые люди зашли так далеко, что предположили, что массивы должны рассматриваться как устаревшие типы, когда у нас есть дженерики. Я не могу сказать, что я готов пойти так далеко, но вы должны подумать о том, является ли коллекция лучшей альтернативой всякий раз, когда вы привязаны к массиву.
Книга Java Generics and Collections от Naftalin и Wadler - отличная ссылка на вопросы, которые могут возникнуть в отношении дженериков. Или, конечно, Общие вопросы - это ваша каноническая онлайн-ссылка.
Ответ 2
Г-н Джош Блох говорит:
"Предпочтительные списки для массива, потому что массивы являются ковариантными, а дженерики - инвариант
Вы могли бы сделать:
List<List<ArrayList>> someListArray;
Это может привести к некоторому результативному результату (даже не отметить, что я ставлю), но при компиляции вы получите лучшую безопасность типов.
но я думаю, что вопрос должен быть больше вокруг "зачем" вам это нужно?
Ответ 3
List<ArrayList>[] someListArray;
дает вам:
array of ( List of ArrayList )
Но из-за ограничений в дженериках Java (ошибка 6229728) вы можете создать только:
array of List
и произведите его:
List<ArrayList>[] someListArray = (List<ArrayList>[]) new List[5];
Ответ 4
Вы правильно говорите:
После запуска некоторых тестов, я определил объявление - это массив, в котором каждый элемент является объектом ArrayList.
Выполнение этого кода
List<ArrayList>[] myArray = new ArrayList[2];
myArray[0] = new ArrayList<String>();
myArray[0].add("test 1");
myArray[1] = new ArrayList<String>();
myArray[1].add("test 2");
print myArray;
Производит этот результат:
{["test 1"], ["test 2"]}
Мне кажется, нет причин не делать этого:
List<ArrayList> myArray = new ArrayList<ArrayList>();
Ответ 5
Список - это список, способный содержать объекты ArrayList
Список [] - это массив таких списков
Итак, вы сказали, что Array of (Список объектов ArrayList) является ПРАВИЛЬНЫМ.
Можете ли вы поделиться своими тестами. Мои собственные тесты разные
import java.util.*;
public class TestList {
public static void main(String ... args) {
class MySpecialLinkedList extends LinkedList<ArrayList<Integer>> {
MySpecialLinkedList() {
}
public void foo() {
}
public Object clone()
{
return super.clone();
}
}
List<ArrayList<Integer>> [] someListArray = new MySpecialLinkedList[10];
for (int i = 0; i < 10; ++i) {
someListArray[i] = new LinkedList<ArrayList<Integer>>();
for (int j = 0; j < 20; ++j) {
someListArray[i].add(new ArrayList<Integer>());
for (int k = 0; k < 30; ++k) {
someListArray[i].get(j).add(j);
}
}
}
}
}
Ответ 6
После выполнения некоторых дополнительных тестов, я думаю, что у меня есть мой ответ.
List <ArrayList> [] действительно указывает массив, в котором каждый элемент является объектом List of ArrayList.
Компиляция кода, как показано ниже, показала, почему мой первый тест позволил мне использовать массив, в котором каждый элемент является списком чего-либо. Использование возвращаемых типов List [] и List в методах, которые заполняют массивы, не предоставило компилятору достаточную информацию для запрета назначений. Но компилятор действительно выдавал предупреждения о двусмысленности.
С точки зрения компилятора метод, возвращающий List [], может возвращать List <ArrayList> (который удовлетворяет декларации), а может и нет. Точно так же метод, возвращающий список, может возвращать ArrayList или не возвращать его.
Здесь был выход компилятора:
javac Generics2.java -Xlint: unchecked
Generics2.java:12: warning: [unchecked] unchecked conversion
found : java.util.List[]
required: java.util.List<java.util.ArrayList>[]
someListArray = getArrayWhereEachElementIsALinkedListObject();
^
Generics2.java:16: warning: [unchecked] unchecked conversion
found : java.util.List[]
required: java.util.List<java.util.ArrayList>[]
someListArray= getArrayWhereEachElementIsAListOfLinkedListObjects();
Вот мои тесты.
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
public class Generics2 {
public static void main(String[] args) {
List<ArrayList>[] someListArray;
someListArray = getArrayWhereEachElementIsALinkedListObject();
// Why does this satisfy the declaration?
//someListArray[0] => LinkedList object holding Strings
someListArray= getArrayWhereEachElementIsAListOfLinkedListObjects();
//someListArray[0] => LinkedList object holding LinkedList objects
}
public static List[] getArrayWhereEachElementIsALinkedListObject() {
List[] arrayOfLists = new LinkedList[2];
arrayOfLists[0] = getStringLinkedListAsList();
arrayOfLists[1] = getIntegerLinkedListAsList();
return arrayOfLists;
}
public static List[] getArrayWhereEachElementIsAListOfLinkedListObjects() {
List list1 = new LinkedList();
list1.add(new LinkedList());
List list2 = new LinkedList();
list2.add(new LinkedList());
List[] arrayOfListsOfLinkedLists = new LinkedList[2];
arrayOfListsOfLinkedLists[0] = list1;
arrayOfListsOfLinkedLists[1] = list2;
return arrayOfListsOfLinkedLists;
}
public static List getStringLinkedListAsList() {
List stringList= new LinkedList();
stringList.add("one");
stringList.add("two");
return stringList;
}
public static List getIntegerLinkedListAsList() {
List intList= new LinkedList();
intList.add(new Integer(1));
intList.add(new Integer(2));
return intList;
}
}