Ошибка неоднозначности при попытке напечатать результат JAVA8 Collector
Я получаю сообщение об ошибке "Неоднозначность" при попытке напечатать результат сборщиков JAVA8.
Я пытаюсь напечатать результат суммирования идентификаторов в Product
объект, но получаю следующую ошибку:
"Метод println(double) неоднозначен для типа PrintStream"
Вот небольшая строка кода, где я получаю ошибку компиляции:
Отредактировано: добавление фрагмента кода для более подробной информации:
- Домен класса Product.java.
пакет com.sample.reproduce.bugs;
public class Product {
private double id;
private String productName;
public double getId() {
return id;
}
public void setId(double id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
}
- Класс Main.java, где я получаю ошибку компиляции:
Ниже приведена строка кода, где я получаю ошибку компиляции:
System.out.println(productList.stream().collect(Collectors.summingDouble(x -> x.getId())));
Снимок класса:
Я не получаю никакой ошибки, если я буду использовать Collector в отдельной строке (из println
метод).
Почему компилятор не может определить точный тип возвращаемого значения сборщиков JAVA 8, если мы используем его в println()
метод?
Добавление деталей другого подхода с помощью командной строки:
Я попытался с помощью командной строки с той же версией JDK, и программа была скомпилирована и выполнена успешно. Так что ответ Хольгера кажется правильным. Это кажется проблемой только с компилятором Eclipse:
2 ответа
Это ошибка в компиляторе Eclipse, и кроличья нора еще глубже, чем ошибка компилятора. Я сократил ваш пример кода до
public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) {}
public static void println(char[] x) {}
public static void println(String x) {}
public static void println(Object x) {}
Я только держал println
методы, влияющие на поведение компилятора.
Есть методы println(Object x)
, который должен быть вызван, так как он применим только без операций бокса, println(double)
, который упоминается в сообщении об ошибке и применяется после распаковки, и два метода println(char[] x)
а также println(String x)
, которые не применимы вообще.
Удаление println(double x)
метод устраняет ошибку, что было бы понятно, даже если ошибка не правильная, но странно, удаление println(Object x)
Метод не решает ошибку.
И что еще хуже, удалив любой из неприменимых методов, println(char[] x)
или же println(String x)
также удаляет ошибку, но генерирует код, вызывающий неправильный неприменимый метод:
public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) { System.out.println("println(double)"); }
public static void println(char[] x) { System.out.println("println(char[])"); }
//public static void println(String x) { System.out.println("println(String)"); }
public static void println(Object x) { System.out.println("println(Object)"); }
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to [C
at Tmp2.main(Unknown Source)
public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) { System.out.println("println(double)"); }
//public static void println(char[] x) { System.out.println("println(char[])"); }
public static void println(String x) { System.out.println("println(String)"); }
public static void println(Object x) { System.out.println("println(Object)"); }
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
at Tmp2.main(Unknown Source)
Я думаю, нам не нужно копаться в глубине формальной спецификации языка Java, чтобы признать это поведение неуместным.
Удаление обоих неприменимых методов, println(char[] x)
а также println(String x)
, заставляет компилятор выбрать правильный метод, println(Object x)
над println(double x)
, но это не впечатляет.
Для справки я тестировал с версией Oxygen.3a Release (4.7.3a), сборка 20180405-1200. Вероятно, затронуты и другие версии.
Да, это ошибка компилятора, но более тщательное исследование показывает, что это может быть вызвано упущением в JLS.
Более конкретно, ошибка исчезла бы, если бы одно предложение в JLS §18.5.2.2. были изменены так:
Старый:
Для выражения создания экземпляра класса poly или выражения вызова метода poly C содержит все формулы ограничения, которые должны появиться в наборе C, созданном §18.5.2, при выводе типа вызова выражения poly.
Упоминание только "форумных ограничений" представляется недостаточным.
Предлагается новый:
Для выражения создания экземпляра класса poly или выражения вызова метода poly C содержит все границы типов и границы захвата, которые могут возникнуть в результате сокращения и включения набора C, сгенерированного §18.5.2, при выводе типа вызова выражения poly.
PS: Javac известен тем, что реализовал больше / разных потоков данных между внутренним и внешним выводом, чем захватывается в JLS, что, вероятно, является причиной, почему Javac выбирает println(Object)
, В некоторых отношениях эта реализация может быть ближе к предполагаемой семантике, и в примере в этом вопросе здравый смысл согласуется с javac. Вот почему фокус ИМХО должен быть на улучшении JLS (и транзитивно ecj).
РЕДАКТИРОВАТЬ: Хотя приведенный выше анализ является правдоподобным, устраняет проблему и, возможно, даже соответствует тому, что фактически делает javac, он не может объяснить, почему проблема возникает только при разрешении перегрузки для println(..)
но не в назначении char[]
переменная.
После дополнительных исследований этой разницы было разработано альтернативное изменение, которое эффективно (через несколько косвенных направлений) заставит компилятор пересчитывать границу захвата вместо того, чтобы передавать ее, как предложено выше. Это изменение соответствует текущему JLS. Точная цепочка причин этой проблемы выходит за рамки этого форума, но заинтересованным сторонам предлагается прочитать некоторые сведения об ошибке Eclipse, связанной выше.
System.out.println(productsList.stream().mapToDouble(x -> x.id).sum());
Я не совсем уверен насчет точного кода здесь, но без обязательного типа (println
имеет много перегруженных параметров) и общую типизацию потока, возникает неоднозначность. Особенно с Double id
вместо double
, Может быть, кто-то может сделать лучшее объяснение.
Присвоение локальной переменной могло бы сработать.
Лучше использовать потоки примитивного типа. Выше используется DoubleStream
, Для идентификатора я бы предпочел LongStream.