Java:如何使用Optional.empty()编译?

Tra*_*kel 5 java java-8

这似乎是一个非常愚蠢的问题,但我无法理解为什么这样使用Optional<T>编译:

import java.util.Optional;

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

    public static void main() {
        foo(Optional.empty());
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

但是,代码编译完全正常.显然,我的思维过程不正确......但在哪里?


让我在这里的答案中澄清我正在寻找的内容......我知道什么是类型推断.我不明白它是如何发生的,以及从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());
    }
}
Run Code Online (Sandbox Code Playgroud)

当你必须处理重载等事情时,它如何在Java 8中工作?是否有Java语言规范的特定部分讨论这种事情?

cog*_*tos 10

这是因为在Optional中定义empty()方法的方式:

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

注意上面的方法类型参数:

public static<T> Optional<T> empty() {
             ^^^ method type parameter
Run Code Online (Sandbox Code Playgroud)

这意味着当调用empty()时,它会将T绑定到其调用者的上下文,在您的情况下为String.有关更多信息,请参阅Java教程中此页面中的目标类型部分:

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


Hol*_*ger 8

由于您的问题的这一部分尚未解决,我将尝试总结Java 7和Java 8之间的变化.

Java 7已经有类型推断,例如你可以编写

List<String> list=Collections.emptyList();
Run Code Online (Sandbox Code Playgroud)

要么

List<String> getList() {
    return Collections.emptyList();
}
Run Code Online (Sandbox Code Playgroud)

但是这种类型的推理相当有限,例如什么不起作用(除了其他)是:

List<String> list=Collections.unmodifiableList(Collections.emptyList());
Run Code Online (Sandbox Code Playgroud)

要么

List<String> getList() {
    return condition? new ArrayList<>(): Collections.emptyList();
}
Run Code Online (Sandbox Code Playgroud)

这两个示例现在在Java 8下工作.这个新特性称为目标类型推断,因为它现在使用目标的类型来查找适当的类型参数.除了使嵌套方法调用和条件工作,如上例中所示,它还修复了以下示例:

List<Number> numbers=Arrays.asList(1, 2, 3, 4);
Run Code Online (Sandbox Code Playgroud)

如上所述,Java 7也有类型推断,但在这个例子中,它会推断出List<Integer>传递给参数的表达式的结果类型,asList从而产生错误.

相比之下,Java 8具有目标类型推断,并将使用赋值的目标类型推断List<Number>为表达式的类型,并发现整个语句是有效的,因为您可以使用期望的Integer对象Number.

请注意Optional.empty()Collections.emptyList()使用相同类型的Generic构造.我在我的示例中使用了后者,因为它已经存在于Java 7中.


NoD*_*und 5

这是因为编译器会进行类型推断。

当编译器读取到:

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

public static void main() {
    foo(Optional.empty());
}
Run Code Online (Sandbox Code Playgroud)
  • 它知道Optional.<T>empty()以 aT作为参数。
  • 它知道foo期望Optional<String>
  • 它推断TString.

它是在引入泛型时引入的,也许它的机制在 Java 8 的javac.

请注意,这取决于编译器:ECJ(Eclipse JDT 编译器)不理解 Javac 理解的相同 java 源(但是,它们编译“相同”字节码)。