这似乎是一个非常愚蠢的问题,但我无法理解为什么使用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语言规范的特定部分讨论这种事情?
答案 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}}个对象。
请注意Number
和Optional.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>
T
为String
。引入泛型时引入它,并且可能使用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");
编译好。即表达式的类型因使用方式而发生变化。