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

时间:2014-09-30 20:32:06

标签: 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());
    }
}

Optional::empty定义为返回Optional<T>。在Driver::main内部,表达式Optional.empty()似乎会返回Optional<Object>,因为我没有参数化Optional的使用,所以我希望它会回归到Object {1}}作为类型参数。然后,我将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语言规范的特定部分讨论这种事情?

4 个答案:

答案 0 :(得分:10)

这是因为在Optional:

中定义empty()方法的方式
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

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

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

答案 1 :(得分: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的{​​{1}}个对象。

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

答案 2 :(得分:0)

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

编译器读取时:

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

public static void main() {
    foo(Optional.empty());
}
  • 它知道Optional.<T>empty()T作为参数。
  • 它知道foo期待Optional<String>
  • 它推断TString

引入泛型时引入它,并且可能使用Java 8 javac改进了它的机制。

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

答案 3 :(得分:-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"); 

编译好。即表达式的类型因使用方式而发生变化。