Невозможно привести из списка <Node>в список <Element> - PullRequest
0 голосов
/ 06 ноября 2018

все. Я практикую dom4j и Xpath, но застрял в проблеме.

Я пытаюсь:

List<Element> conList = (List<Element>)doc.selectNodes("//contact");

но получил ошибку:

Cannot cast from List<Node> to List<Element>

В обучающем видео код работает нормально, но не работал на моем компьютере.

Это какая-то незаконная операция? Могу ли я решить проблему другим способом? Спасибо.

Ответы [ 2 ]

0 голосов
/ 06 ноября 2018

Это неприятная проблема, потому что вы знаете (из семантики выражения XPath), что все элементы в списке будут экземплярами Element, но нет никакого способа сообщить об этом компилятору Java. Если бы разработчики DOM4J подумали немного более тщательно, они бы объявили результат как List<? extends Node>, что дало бы вам немного больше гибкости (хотя приведение все равно дало бы вам предупреждения компилятора). Это также разочаровывает, потому что причина этого правила заключается в опасности добавления неподходящих элементов в список, и что вам действительно нужно, так это неизменный список, в котором проблема не должна возникать (хотя это все же происходит, потому что генерики не обрабатывают неизменные коллекции специально).

Чистое решение, предоставленное @MagnusLutz, удовлетворит компилятор, но это дорого - оно требует копирования списка.

В Saxon 9.9 я разработал новый API, который пытается хорошо играть с дженериками, и он работает с DOM4J, так что попробуйте. Это также позволяет избежать затрат на компиляцию / интерпретацию выражения XPath для таких простых случаев, как этот. Но (к сожалению) это не решает основные ограничения обобщенных типов, особенно в отношении таких конструкций, как выражения XPath, где информация о типе, доступная для компилятора XPath, не может быть передана компилятору Java.

Что вы на самом деле хотите сделать с элементами? Если бы мне нужно было передать список методу, который требует List<Element>, я бы, вероятно, использовал решение из @MagnusLutz. Если бы я просто хотел обработать элементы, я бы сделал

for (Node n : doc.selectNodes("//contact")) {
   Element e = (Element)n;
   ...
}
0 голосов
/ 06 ноября 2018

Вы не можете просто привести объект на основе обобщенного типа с конкретным параметром таким образом.

Хороший способ достичь своей цели в java8:

List<Element> conList = doc.selectNodes("//contact")
.stream()
.map(node->(Element)node)
.collect(Collectors.toList());

Остерегайтесь того, что в общем случае, когда вы не знаете, являются ли элементы списка на самом деле экземпляром вашего целевого класса или интерфейса, вы, возможно, захотите подтвердить это, отфильтровав

List<Element> conList = doc.selectNodes("//contact")
.stream()
.filter(node->node instanceof Element)
.map(node->(Element)node)
.collect(Collectors.toList());
...