Kotlin 错误地推断 JOOQ 方法
Kotlin infers JOOQ methods wrong
给定一个使用 Kotlin 版本 1.3.61 和 JOOQ 版本 3.13.1 的系统,这样的方法可以正常构建 union
查询:
val selectCommonPart = coalesce(sum(field(name("amount"), Long::class.java)), ZERO)
.`as`(field(name("totalAmount")))
var whereCommonPart: Condition = trueCondition().and(field(name("Id")).eq(Id)) // Id comes as a parameter
var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
query.addConditions(whereCommonPart)
// `endDate` is also a `LocalDate`, can be either equal to `startDate` or after
if (endDate.isAfter(startDate)) {
for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
.limit(ChronoUnit.DAYS.between(startDate, endDate))
.collect(Collectors.toList())) {
val unionQuery = dsl.selectQuery()
unionQuery.addSelect(selectCommonPart)
unionQuery.addFrom(table("public.${tableName(date)}"))
unionQuery.addConditions(whereCommonPart)
// Here `union` is inferred correctly
query.union(dsl.select(selectCommonPart)
.from("public.public.${tableName(date)}")
.where(whereCommonPart))
}
}
但是,如果我在如下方法中隔离 dsl.select(...)
部分:
private fun buildSelect(selectCommonPart: Field<*>, whereCommonPart: Condition, date: LocalDate): Select<*> {
var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("public.${tableName(date)}"))
query.addConditions(whereCommonPart)
return query
}
并修改循环以使用:
// Remove this part
/* var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
query.addConditions(whereCommonPart) */
// Add this part
var query = buildSelect(selectCommonPart, whereCommonPart, startDate)
if (endDate.isAfter(startDate)) {
for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
.limit(ChronoUnit.DAYS.between(startDate, endDate))
.collect(Collectors.toList())) {
// This gives an inference error
query.union(buildSelect(selectCommonPart, whereCommonPart, date))
}
}
我有一个推理错误。 Kotlin 将 union
解析为此方法:
/**
* Returns a set containing all distinct elements from both collections.
*
* The returned set preserves the element iteration order of the original collection.
* Those elements of the [other] collection that are unique are iterated in the end
* in the order of the [other] collection.
*
* To get a set containing all elements that are contained in both collections use [intersect].
*/
public infix fun <T> Iterable<T>.union(other: Iterable<T>): Set<T> {
val set = this.toMutableSet()
set.addAll(other)
return set
}
我想改用JOOQ的Select<*>
的union
:
public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
/**
* Apply the <code>UNION</code> set operation.
*/
@Support
Select<R> union(Select<? extends R> select);
我应该怎么做才能推断出正确的union
方法?
好的,我知道如何解决你的问题。
方法构建Select应该returnsSelect<记录>,而不是Select< *>.
我的建议为什么会这样:
Select.union 方法具有以下签名
public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
@Support
Select<R> union(Select<? extends R> var1);
....
如您所见,var1 应该与调用方法的对象具有相同的泛型(或扩展)类型。在您的第一个实现中,方法 dsl.selectQuery() returns SelectQuery< Record> 两者都是变量 query 和 unionQuery 具有相同的泛型类型并且正确确定了 union 方法。
在第二个实现中,查询和query.union(...)的参数有Select< *>类型。我猜想编译器认为这两个变量具有不同的泛型(因为它未确定并且可以不同)并且编译器不能使用来自 Select 的联合,但是两个变量都实现了 Iterable 和编译器选择 Iterable.union 因为它适合。
问题是对于 Select
的 union
参数的类型参数必须是接收者 (? extends R
) 的子类型;如果两者都是Select<*>
,它可能是例如Select<Int>.union(Select<String>)
不会进行类型检查。 Iterable
不会发生这种情况,因为它是协变的。
我认为 buildSelect
可以更准确地键入
private fun <T : Record> buildSelect(selectCommonPart: Field<T>, whereCommonPart: Condition, date: LocalDate): Select<T>
然后因为两个调用都有相同的第一个参数,所以应该可以解决。
编辑:我只是假设 selectCommonPart
的类型和返回的查询必须匹配,但是 they don't 和 dsl.selectQuery()
returns Select<Record>
,所以Maxim Popov 回答中的签名是正确的。
给定一个使用 Kotlin 版本 1.3.61 和 JOOQ 版本 3.13.1 的系统,这样的方法可以正常构建 union
查询:
val selectCommonPart = coalesce(sum(field(name("amount"), Long::class.java)), ZERO)
.`as`(field(name("totalAmount")))
var whereCommonPart: Condition = trueCondition().and(field(name("Id")).eq(Id)) // Id comes as a parameter
var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
query.addConditions(whereCommonPart)
// `endDate` is also a `LocalDate`, can be either equal to `startDate` or after
if (endDate.isAfter(startDate)) {
for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
.limit(ChronoUnit.DAYS.between(startDate, endDate))
.collect(Collectors.toList())) {
val unionQuery = dsl.selectQuery()
unionQuery.addSelect(selectCommonPart)
unionQuery.addFrom(table("public.${tableName(date)}"))
unionQuery.addConditions(whereCommonPart)
// Here `union` is inferred correctly
query.union(dsl.select(selectCommonPart)
.from("public.public.${tableName(date)}")
.where(whereCommonPart))
}
}
但是,如果我在如下方法中隔离 dsl.select(...)
部分:
private fun buildSelect(selectCommonPart: Field<*>, whereCommonPart: Condition, date: LocalDate): Select<*> {
var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("public.${tableName(date)}"))
query.addConditions(whereCommonPart)
return query
}
并修改循环以使用:
// Remove this part
/* var query = dsl.selectQuery()
query.addSelect(selectCommonPart)
query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
query.addConditions(whereCommonPart) */
// Add this part
var query = buildSelect(selectCommonPart, whereCommonPart, startDate)
if (endDate.isAfter(startDate)) {
for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
.limit(ChronoUnit.DAYS.between(startDate, endDate))
.collect(Collectors.toList())) {
// This gives an inference error
query.union(buildSelect(selectCommonPart, whereCommonPart, date))
}
}
我有一个推理错误。 Kotlin 将 union
解析为此方法:
/**
* Returns a set containing all distinct elements from both collections.
*
* The returned set preserves the element iteration order of the original collection.
* Those elements of the [other] collection that are unique are iterated in the end
* in the order of the [other] collection.
*
* To get a set containing all elements that are contained in both collections use [intersect].
*/
public infix fun <T> Iterable<T>.union(other: Iterable<T>): Set<T> {
val set = this.toMutableSet()
set.addAll(other)
return set
}
我想改用JOOQ的Select<*>
的union
:
public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
/**
* Apply the <code>UNION</code> set operation.
*/
@Support
Select<R> union(Select<? extends R> select);
我应该怎么做才能推断出正确的union
方法?
好的,我知道如何解决你的问题。
方法构建Select应该returnsSelect<记录>,而不是Select< *>.
我的建议为什么会这样:
Select.union 方法具有以下签名
public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
@Support
Select<R> union(Select<? extends R> var1);
....
如您所见,var1 应该与调用方法的对象具有相同的泛型(或扩展)类型。在您的第一个实现中,方法 dsl.selectQuery() returns SelectQuery< Record> 两者都是变量 query 和 unionQuery 具有相同的泛型类型并且正确确定了 union 方法。
在第二个实现中,查询和query.union(...)的参数有Select< *>类型。我猜想编译器认为这两个变量具有不同的泛型(因为它未确定并且可以不同)并且编译器不能使用来自 Select 的联合,但是两个变量都实现了 Iterable 和编译器选择 Iterable.union 因为它适合。
问题是对于 Select
的 union
参数的类型参数必须是接收者 (? extends R
) 的子类型;如果两者都是Select<*>
,它可能是例如Select<Int>.union(Select<String>)
不会进行类型检查。 Iterable
不会发生这种情况,因为它是协变的。
我认为 buildSelect
可以更准确地键入
private fun <T : Record> buildSelect(selectCommonPart: Field<T>, whereCommonPart: Condition, date: LocalDate): Select<T>
然后因为两个调用都有相同的第一个参数,所以应该可以解决。
编辑:我只是假设 selectCommonPart
的类型和返回的查询必须匹配,但是 they don't 和 dsl.selectQuery()
returns Select<Record>
,所以Maxim Popov 回答中的签名是正确的。