Почему компилятор Java не может это понять?

Почему компилятор не может определить правильный тип результата из Collections.emptySet () в следующем примере?

import java.util.*;
import java.io.*;

public class Test {
    public interface Option<A> {
        public <B> B option(B b, F<A,B> f);
    }

    public interface F<A,B> {
        public B f(A a);
    }

    public Collection<String> getColl() {
        Option<Integer> iopt = null;

        return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
            public Collection<String> f(Integer i) {
                return Collections.singleton(i.toString());
            }
        });
    }
}

Вот сообщение об ошибке компилятора:

knuttycombe@knuttycombe-ubuntu:~/tmp/java$ javac Test.java 
Test.java:16: <B>option(B,Test.F<java.lang.Integer,B>) in 
Test.Option<java.lang.Integer> cannot be applied to (java.util.Set<java.lang.Object>,
<anonymous Test.F<java.lang.Integer,java.util.Collection<java.lang.String>>>)
            return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
                   ^
1 error

Теперь, конечно, работает следующая реализация getColl ():

    public Collection<String> getColl() {
        Option<Integer> iopt = null;

        Collection<String> empty = Collections.emptySet();
        return iopt.option(empty, new F<Integer, Collection<String>>() {
            public Collection<String> f(Integer i) {
                return Collections.singleton(i.toString());
            }
        });
    }

и вся цель методов безопасного типа в коллекциях состоит в том, чтобы избежать такого рода проблем с одноэлементными коллекциями (в отличие от использования статических переменных). Итак, компилятор просто не может выполнять логический вывод на нескольких уровнях универсальных шаблонов? Что происходит?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
0
1 186
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Для вывода Java требуется много усилий. Во многих случаях система типов могла бы сделать вывод лучше, но в вашем случае будет работать следующее:

print("Collections.<String>emptySet();");

Пример, в котором Java могла бы работать лучше и рассматривается для Java 7, выглядит примерно так: Map <String, Integer> model = new HashMap <> ();

GaryF 23.12.2008 01:54

Замечательно, я раньше не видел такого синтаксиса. Спасибо!

Kris Nuttycombe 23.12.2008 01:56

Просто используйте GenericFactory, который был представлен Блохом во 2-м издании Effective Java ...

André 23.12.2008 01:57

Похоже на проблему приведения типов, то есть на то, что требуется преобразовать ObjectSet<Object>, который был бы типом пустого набора) в String. Принятие решений в общем случае небезопасно.

Collections.emptySet() не является Collection<String>, если Java не знает, что ему там нужен Collection<String>. В этом случае кажется, что компилятор несколько глупо говорит о порядке, в котором он пытается определить типы, и пытается определить тип возвращаемого значения Collections.emptySet(), прежде чем пытаться определить предполагаемый тип параметра шаблона для B на самом деле String.

Решение состоит в том, чтобы прямо заявить, что вам нужен Collections.<String>emptySet(), как упоминал GaryF.

Сначала вы можете сузить проблему до этого кода:

public class Test { 
    public void option(Collection<String> b) {
    }

    public void getColl() {
        option(Collections.emptySet());
    }
}

Это не работает, вам нужна временная переменная, иначе компилятор не сможет определить тип. Вот хорошее объяснение этой проблемы: Почему временные переменные имеют значение в случае вызова общих методов?

Хорошая статья. В частности, ключевой момент, который отвечает на исходный вопрос, выглядит следующим образом: «вызов метода не учитывается для вывода типа».

Dave Costa 23.12.2008 17:48

Другие вопросы по теме