Вопрос по treenode, initialization, java, arrays, generics – Ошибка Java: новый универсальный массив TreeNode

1

У меня есть общий класс TreeNode:

public class TreeNode<E> {
public E key;
public int num_of_children;
public TreeNode<E> [] children;


public TreeNode(int num_of_children)
{
    this.num_of_children = num_of_children;
    children = new TreeNode[num_of_children];// Why not: new TreeNode<E>[num_of_children]?
}

public TreeNode<E> clone()
{
    TreeNode<E> node = new TreeNode<E>(num_of_children);
    return node;
}

}

Когда я пытаюсь сделать:children = new TreeNode<E> [num_of_children];

Я получаю ошибку. Но & quot; новый TreeNode [num_of_children] & quot; работает. Я прочитал об удалении типа и не понимаю, почемуTreeNode<E>[] не работает. Это почему? Пожалуйста, просветите меня!

Обобщения и массивыreally don't play well together.  Избегайте использования обобщений с массивами. Louis Wasserman

Ваш Ответ

2   ответа
1

Потому что спецификация языка Javaпишет:

An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or ClassOrInterfaceType.

It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).

The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.

Мне не понятно, зачем им это нужно. Разумеется, тип компонента массива должен быть доступен во время выполнения, и это может ввести в заблуждение программиста, если он отличается от типа, указанного в исходном коде. Рассматривать:

E[] a = new E[10];

Здесь было бы плохо, если бы компилятор использовал стираниеE как тип компонента массива, так как программист вполне может зависеть от массива, чтобы проверить, что ничего, кроме экземпляровE хранится в нем.

Менее ясно, какой вред может принести разрешение:

List<E>[] lists = new List<E>[10];

Единственное, что приходит на ум, это то, что назначение элемента массива будет равносильно непроверенному приведению, потому что массив будет проверять элемент какListно не то чтобы этоList<E>и, следовательно, не в состоянии броситьArrayStoreException.

На практике вы можете безопасно подавить это предупреждение, если будете помнить, что массив не будет проверять параметры типа своего компонента.

2

Вещи какnew TreeNode<String>[] а такжеnew TreeNode<E>[] не разрешены Java. Единственное, что вы можете сделать, этоnew TreeNode[] а такжеnew TreeNode<?>[] (неограниченный подстановочный параметр).

Причина этого немного сложная, но поучительная. Массивы в Java знают свой тип компонента во время выполнения, и каждый раз, когда вы помещаете что-либо, он проверяет, является ли он экземпляром типа компонента, и, если нет, выдает исключение (это связано с тем, как типы массивов являются ковариантными. и, следовательно, изначально небезопасны во время компиляции).

Object[] foo = new Integer[5];
foo[2] = "bar"; // compiles fine, but throws ArrayStoreException at runtime

Теперь добавьте дженерики. Проблема с общим типом компонента заключается в том, что вы не можете проверить, является ли объект экземпляром, скажем,TreeNode<Integer> во время выполнения (в отличие отTreeNode<String>), поскольку генерики удаляются из типов времени выполнения. Это может только проверитьTreeNode, но не тип компонента. Но программисты, возможно, ожидали такого поведения проверки и выброса исключений из массивов, поскольку оно обычно работает. Поэтому, чтобы избежать этого неожиданного сбоя, Java запрещает его. (В большинстве кодов вы все равно не столкнетесь с этой проблемой, потому что не будете смешивать объекты одного типа, но с разными параметрами типа. Но теоретически это возможно.)

Конечно, вы можете просто обойти эту проблему, создав массив необработанных или групповых типов параметров, а затем приведя к нужному типу, например,(TreeNode<Integer>)new TreeNode[5], В чем разница? Что ж, это неконтролируемое приведение, которое генерирует предупреждение, и вы, программист, берете на себя ответственность за все небезопасные вещи, которые могут произойти позже. Если он делает что-то неожиданное, компилятор может сказать: «Мы так сказали!».

Похожие вопросы