Я читаю о Дженериках Java, и я столкнулся с этой темой, где я немного смущен.
От: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205
public abstract class Node > {
private final List children = new ArrayList();
private final N parent;
protected Node(N parent) {
this.parent = parent;
parent.children.add(this); // error: incompatible types
}
public N getParent() {
return parent;
}
public List getChildren() {
return children;
}
}
public class SpecialNode extends Node {
public SpecialNode(SpecialNode parent) {
super(parent);
}
}
Прокрутка ниже несколько экранов...
public abstract class Node > {
...
protected Node(N parent) {
this.parent = parent;
parent.children.add( (N)this ); // warning: unchecked cast
}
...
}
Броски, целевой тип которых является параметром типа, не могут быть проверены во времени выполнения и выводе к предупреждению непроверенному. Этот небезопасный бросок представляет потенциал для неожиданного ClassCastException s и лучше всего избегается.
Кто-то мог дать мне пример, где вышеупомянутый код бросает ClassCastException?
Спасибо.
В первом примере кода присутствует компиляция-ошибка. Вы можете проверить его самостоятельно в своей IDE.
Мой говорит : Метод add(N) в типе List
Проблема в том, что N является подтипом Node. Список N может быть списком StupidNode, где StupidNode является подклассом Node. Но текущий экземпляр может и не быть StupidNode, это может быть другой подкласс Node, поэтому добавление может быть неправильным.
Теперь второй пример кода - тот, где разработчик, раздосадованный ошибкой времени компиляции, которую он не понимает, считает, что компилятор ошибается и пытается форсировать кастинг. Такой слепок заставляет код компилироваться, но может сломаться во время выполнения в тех же условиях (как объяснено выше).
Следовательно, компилятор выдает предупреждение, чтобы помочь понять, что что-то может быть не так.
Для обоих предыдущих примеров кода проблема может возникнуть, если вызывающий код пишет (для двух подклассов NodeA
и NodeB
из Node
):
Node<NodeA> root = new NodeA<NodeA>(null);
// code needs a change, to be able to handle the root, that has no parent
// The line with parent.children will crash with a NullPointerException
Node<NodeB> child = new NodeB<NodeB>(root);
Во второй строке код, который будет запущен в конструкторе Node
, будет интерпретироваться подобно (заменив параметр форматирования N
текущим параметром NodeB
):
public abstract class Node <NodeB> {
private final List<NodeB> children = new ArrayList<NodeB>();
private final NodeB parent;
protected Node(NodeB parent) {
this.parent = parent;
parent.children.add(this); // error: incompatible types
}
// ...
}
Как видно, вторая строка вызывающего абонента будет передавать экземпляр NodeA
, в то время как конструктор Node
ожидает NodeB
! Отсюда и ошибка...
UPDATE, о которой просит комментарий : пример кода для подклассов NodeA (или NodeB).
public class NodeA extends Node<NodeA> {
public NodeA(NodeA parent) {
super(parent);
}
}