我有一个界面Foo和一个实现Foo的枚举栏。然后我使用一个简单的构建器模式类Result。请参阅下面的代码。
public interface Foo {}
public enum Bar implements Foo { BAR }
public class Result<T> {
public Result(T result) {}
public static <T> Result<T> of(T result) {
return new Result<>(result);
}
public Result<T> set() {
return this;
}
}
尝试构建Bar.BAR类型的Result并将其分配给Result&lt; Foo&gt;变量这很好用:
Result<Foo> r1 = Result.of(Bar.BAR);
但是下面给出了一个编译错误:
Result<Foo> r2 = Result.of(Bar.BAR).set();
不兼容的类型。必填:结果&lt; Foo&gt ;,找到:结果&lt; Bar&gt;
有人可以解释一下原因吗?
答案 0 :(得分:3)
有人可以解释一下原因吗?
因为泛型很难,编译器很难猜测你的意思。
它在Java 8中变得更好。你的第一次尝试将无法在Java 7及更低版本上进行编译。
of
是一种通用方法。 Java上的类型推断会查看周围的上下文,在这种情况下,指定的变量的类型,以确定目标类型参数。在您的示例中,它可以推断Foo
,因为Bar.BAR
是Foo
的子类型。
在你的第二个例子中,链接方法使事情变得更加困难。 set
不是通用方法。因此,它取决于它所调用的表达式的类型。由于方法链接,编译器不能依赖于of
调用的上下文。它采用了它所看到的类型,即。 Bar
Bar.BAR
。然后调用变为(澄清类型)
((Result<Bar>) Bar.BAR).set();
其中set
的返回类型为Bar
。
因为
Result<Bar>
不是Result<Foo>
,因此一个表达式不能分配给另一个。
答案 1 :(得分:3)
由于Java 8的目标类型推断,第一个示例进行了编译,因为T
被推断为Foo
,而不是Bar
。
Result<Foo> r1 = Result.of(Bar.BAR);
它编译是因为Bar
是Foo
,因此它可以作为参数传递给of
方法。
第二个例子没有编译,因为T
必须推断为Bar
,而不是Foo
。
Result<Foo> r1 = Result.of(Bar.BAR).set();
在赋值运算符将结果赋给set()
之前调用r1
方法。在此,Result.of(Bar.BAR).set()
必须单独考虑,而不考虑r1
的类型,因此T
被推断为Bar
。
此外,Java的泛型是不变的,因此即使Bar
是Foo
,Result<Bar>
也不是Result<Foo>
。但是你可以使用通配符来解决这种情况。
Result<? extends Foo> r1 = Result.of(Bar.BAR).set();
你的第一个例子当然是另一种解决方法。
Paul Boddington的评论中提到的另一种解决方法是使用泛型方法of
的显式类型参数。这明确将T
设置为Foo
。
Result<Foo> r2 = Result.<Foo>of(Bar.BAR).set();
此外,这不是Builder Pattern;您的of
方法只是一种工厂方法。 Builder Pattern使用一个单独的类,其目的是构造目标类的实例。
public class Result<T> {
// Prevent anyone except the Builder class from instantiating
// this class by making the constructor private.
private Result(T result) {}
public static class Builder<T>
{
private T result;
public void setResult(T result)
{
this.result = result;
}
public Result<T> build()
{
return new Result(result);
}
}
public Result<T> set() {
return this;
}
}