2014-09-30 2 views
3

Это кажется очень глупый вопрос, но я не могу понять, почему это использование Optional<T> компилирует:Java: Как используется опция Optional.empty()?

import java.util.Optional; 

public class Driver { 
    static void foo(Optional<String> x) { } 

    public static void main() { 
     foo(Optional.empty()); 
    } 
} 

Optional::empty определяется как возвращение мне Optional<T>. Внутри Driver::main выражение Optional.empty() похоже, что оно вернет Optional<Object>, так как я не параметризую использование Optional, поэтому я ожидаю, что он вернется к Object в качестве параметра типа. Затем я передаю Optional<Object> функции, которая ожидает Optional<String>, что является понижающим параметром, который не должен быть разрешен. Я бы ожидал увидеть что-то вроде:

incompatible types: Optional<Object> cannot be converted to Optional<String> 

Однако код компилируется отлично. Ясно, что мой мыслительный процесс здесь неверен ... но где?


Позвольте мне уточнить, что я ищу в ответе здесь ... Я знаю, что такое тип вывода. То, что я не понимаю , как это происходит, здесь происходит и что изменилось на языке Java 7 на Java 8. Например, этот бит кода отлично компилируется в Java 8, но с ошибкой в ​​Java 7:

final class Opt<T> { 
    private final T value; 

    Opt(T x) { 
     value = x; 
    } 

    public static <T> Opt<T> empty() { 
     return new Opt<T>(null); 
    } 
} 

public class Driver { 
    static void bar(Opt<String> x) { } 

    public static void main() { 
     bar(Opt.empty()); 
    } 
} 

Как это работает на Java 8, когда вам приходится иметь дело с вещами, такими как перегрузка? Есть ли определенный раздел спецификации языка Java, который говорит об этом?

+3

Тип вывода - это классная вещь –

+0

Это не отвечает на вопрос полезным. –

ответ

9

Это из-за способа, как метод пустой() определяется в Опционально:

public static<T> Optional<T> empty() { 
    @SuppressWarnings("unchecked") 
    Optional<T> t = (Optional<T>) EMPTY; 
    return t; 
} 

Примечание параметр типа метода выше:

public static<T> Optional<T> empty() { 
      ^^^ method type parameter 

Это означает, что, когда пустой() называется , он привяжет T к контексту своего вызывающего, в вашем случае String. Для получения дополнительной информации смотрите раздел целевых видов на этой странице в Java Tutorial:

http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

+0

+1 по параметру типа. Однако не так уверен в части вывода. Я имею в виду, что объявление параметра типа довольно явное. – rompetroll

-1

Java 8 интерфейс добавленного типа, что означает, что он выработает тип выражения на основе того, как он используется.

blatent пример

Object o =() -> System.out.println("Hello World"); 

не компилируется, потому что он не знает тип выражения однако,

Runnable r =() -> System.out.println("Hello World"); 

Object o = (Runnable)() -> System.out.println("Hello World"); 

компилироваться. т.е. тип выражения изменяется из-за того, как он используется.

0

Это потому, что компилятор делает вывод типа.

Когда компилятор прочитал:

static void foo(Optional<String> x) { } 

public static void main() { 
    foo(Optional.empty()); 
} 
  • Он знает, что Optional.<T>empty() взять T в качестве параметра.
  • Он знает, что foo ожидать Optional<String>
  • Он делает вывод, что T является String.

Это было введено, когда генерические средства, которые были введены, и, возможно, его механизм был улучшен с помощью Java 8's javac.

Обратите внимание, что это зависит от компилятора: ECJ (Eclipse JDT Compiler) не понимает тот же java-источник, что понимает Javac (они, однако, компилируют «тот же» байт-код).

8

Так что часть вашего вопроса не рассматривался, я попытаюсь подвести итог, что изменилось между Java 7 и Java 8.

Java 7 уже вывод типа, например, вы могли бы написать

List<String> list=Collections.emptyList(); 

или

List<String> getList() { 
    return Collections.emptyList(); 
} 

Но этот тип вывода был весьма ограничен, например, что не работает (помимо других) было:

List<String> list=Collections.unmodifiableList(Collections.emptyList()); 

или

List<String> getList() { 
    return condition? new ArrayList<>(): Collections.emptyList(); 
} 

Эти два примера работают теперь под Java 8. Эта новая функция называется целевой тип вывода как теперь использует Таргет чтобы найти соответствующие аргументы типа. Помимо создания вложенных вызовов методов и условные работ, как в приведенных выше примерах, он также фиксирует следующий пример:

List<Number> numbers=Arrays.asList(1, 2, 3, 4); 

Как сказал, Java 7 был типа умозаключения тоже, но в этом примере было бы сделать вывод List<Integer>, как результат результата выражения из аргументов, переданных в asList, и, следовательно, генерировать ошибку.

В отличие от Java 8 имеет целевой тип вывода и будет использовать целевой тип задания, чтобы вывести List<Number> как тип выражения и выяснить, что все утверждение справедливо, как вы можете использовать Integer объекты, где Number ожидается.

Обратите внимание, что Optional.empty() и Collections.emptyList() используют такую ​​же общую конструкцию. Я использовал последний в своих примерах, поскольку он уже существует в Java 7.