Почему компилятор Java не может понять это? - PullRequest
3 голосов
/ 23 декабря 2008

Почему компилятор не может определить правильный тип для результата из 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());
            }
        });
    }

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

Ответы [ 4 ]

8 голосов
/ 23 декабря 2008

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

print("Collections.<String>emptySet();");
5 голосов
/ 23 декабря 2008

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

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

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

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

1 голос
/ 23 декабря 2008

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

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

0 голосов
/ 23 декабря 2008

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

...