在下面的代码中,我的直觉是变量 x 和 y 具有相同的类型:
var x = field(select(T.A).from(T));
var y = select(T.A).from(T).asField();
如果我要求 IntelliJ 将
var
替换为显式类型,我会得到:
Field<String> x = field(select(T.A).from(T));
Field<Object> y = select(T.A).from(T).asField();
我相信 IntelliJ 而不是我的直觉,所以现在我想知道为什么。 这是 Java 的限制吗? jOOQ 中的错误?还有别的吗?
以下是
field
和 asField
的方法签名供参考:
public static <T> Field<T> field(Select<? extends Record1<T>> select);
public <T> Field<T> asField();
DSL.field()
是一个通用方法,可以捕获其类型变量T
。 FieldLike.asField()
无法根据您使用的 FieldLike
的任何子类型(包括 Select<R>
)捕获类型变量。由于历史性错误,它仍然是通用记录,这允许您在使用站点不安全地转换 T
类型。但这只是一个不安全的转换,而不是正确捕获您想要的类型String
由于 jOOQ API 是现在设计的,因此无法在 Java 中修复
asField()
方法,因为 R
中的类型变量 Select<R>
并不知道它是否实际上是一个 Record1<T>
(标量子查询),或其他任何东西。
其他语言可以在其泛型类型边界上添加额外的“约束”,或者允许使用扩展方法来给人一种
.asField()
“方法”的感觉,这实际上只是一个 static
函数,如 DSL.field()
,但在第一个参数上带有后缀符号。
Java 中唯一有效的解决方案是“重载”
Select
类型:
Select1<T1, R extends Record1<T1>> extends Select<R>
Select2<T1, T2, R extends Record2<T1, T2>> extends Select<R>
Select3<T1, T2, T3, R extends Record3<T1, T2, T3>> extends Select<R>
但这会在整个 jOOQ API 中产生大量额外的、不良的副作用,这就是这个想法被拒绝并引入
DSL.field()
的原因。
逻辑上,这两种方法是等效的,但
DSL.field()
更优越,因为它捕获了正确的类型。