Ошибка неоднозначности при попытке напечатать результат JAVA8 Collector - PullRequest
0 голосов
/ 21 февраля 2019

При попытке напечатать результат сборщиков JAVA8 я получаю ошибку неоднозначности.

Я пытаюсь распечатать результат суммирования идентификаторов в Product объекте, но получаю следующую ошибку:

«Метод println (double) неоднозначен для типа PrintStream»

Вот небольшая строка кода, в которой я получаю сообщение об ошибке компиляции:

Отредактировано: добавление фрагмента кода для получения более подробной информации:

  1. Класс домена 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())));

Снимок класса:

enter image description here

Я не получаю никакой ошибки, если буду использовать Collector в отдельной строке (из метода println).

Почему компилятор не может определить точный тип возвращаемого значения сборщиков JAVA 8, если мы используем его в методе println()?

Добавление деталей другого подхода с помощью командной строки:

Я попытался в командной строке с той же версией JDK, и программа была скомпилирована и успешно выполнена.Так что ответ Хольгера кажется правильным.Это кажется проблемой только с компилятором Eclipse:

enter image description here

Ответы [ 3 ]

0 голосов
/ 21 февраля 2019

Это ошибка в компиляторе 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.Возможно, затронуты и другие версии.

0 голосов
/ 06 марта 2019

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

В частности, ошибка исчезнет, ​​если одно предложение в JLS §18.5.2.2. были изменены следующим образом:

Старый:

Для выражения создания экземпляра класса poly или выражения вызова метода poly C содержитвсе формулы ограничения, которые будут появляться в наборе C, сгенерированном §18.5.2, при выводе типа вызова выражения poly.

Упоминание только "форумов ограничений" представляется недостаточным.

Предложено новое:

Для выражения создания экземпляра класса poly или вызова метода polyВ выражении C содержатся все границы типа и границы захвата, которые могут возникнуть в результате уменьшения и включения набора C, сгенерированного §18.5.2, при выводе типа вызова поли-выражения.

PS: Javac известен тем, что реализовал больше / разных потоков данных между внутренним и внешним выводом, чем захватывается в JLS, что, вероятно, является причиной, по которой javac выбирает println(Object).В некоторых отношениях эта реализация может быть ближе к предполагаемой семантике, и в примере в этом вопросе здравый смысл согласуется с javac.Вот почему ИМХО следует сосредоточиться на улучшении JLS (и транзитивно ecj).

EDIT : Хотя приведенный выше анализ правдоподобен, он устраняет проблему и, возможно, даже соответствует тому, что такое javacна самом деле, это не может объяснить, почему проблема возникает только при разрешении перегрузки для println(..), но не при присвоении переменной char[].

После дополнительных исследований этой разницы было разработано альтернативное изменение,это эффективно (через несколько косвенных направлений) заставит компилятор пересчитывать границу захвата вместо передачи его, как предложено выше.Это изменение соответствует текущему JLS.Точная цепочка причин этой проблемы выходит за рамки этого форума, но заинтересованным сторонам предлагается прочитать некоторые сведения об ошибке Eclipse, связанной выше.

0 голосов
/ 21 февраля 2019
System.out.println(productsList.stream().mapToDouble(x -> x.id).sum());

Я не совсем уверен насчет точного кода здесь, но без требуемого типа (println имеет много перегруженных параметров) и общей типизации потока, возникает неоднозначность.Особенно с Double id вместо double. Может быть, кто-то другой может сделать лучшее объяснение.

Возможно, сработало присвоение локальной переменной.

Лучше использовать потоки примитивного типа.Выше используется DoubleStream.Для идентификатора я бы предпочел LongStream.

...