为什么这个没有重载?

4

撰写Jooq Helper类。(在我弄清楚之后,将添加更多业务特定的方法...)

import org.jooq.*
import org.springframework.stereotype.Repository
import javax.inject.Inject
import javax.inject.Provider

/**A helper class facilitating database manipulations.
 * Uses jOOQ for its manipulations.
 * Relies on the application context being properly initialized s.t. Spring creates and injects the correct
 * [DSLContext].*/
@Repository
class DatabaseManipulator{

    private val provider: Provider<DSLContext>
    private val create:DSLContext
        get() = provider.get()

    @Inject
    constructor(provider: Provider<DSLContext>) {
        this.provider = provider
    }


    /**Executes non-[Select]-type SQL queries on the database.
     * Does not perform any kind of checks and trusts the client to know what they're doing.
     * @param query the query to be executed.
     *
     * @return depending on the type of the [query]:
     *      <ul>
     *      <li> Delete : the number of deleted records</li>
     *      <li> Insert : the number of inserted records</li>
     *      <li> Merge : result may be meaningless</li>
     *      <li> Truncate : result may be meaningless</li>
     *      <li> Update : the number of updated records</li>
     *      </ul>
     */
    fun execute(query:DSLContext.()-> Query):Int{
        return create.query().execute()
    }

    /**Executes [Select]-type [query] and returns its result*/
    fun <T:Record> execute(query:DSLContext.()->Select<T>):Result<T>{
        return create.query().fetch()
    }
}

目前为止,一切都很好。现在让我们添加一些测试。

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest
@Transactional
class DatabaseManipulatorIT {

    @Inject
    private lateinit var manipulate:DatabaseManipulator

    @Inject
    private lateinit var em:EntityManager

    @Test
    fun executeSelectQuery() {
        //given
        val expectedNumberOfOrganizations = em.createQuery("SELECT COUNT (o) FROM Organization o")
            .singleResult as Long

        //when
        val reportedNumberOfOrganizations = manipulate.execute {
            selectCount().from(ORGANIZATION)
        }.first().value1().toLong()

        //then
        assertThat(reportedNumberOfOrganizations).isEqualTo(expectedNumberOfOrganizations)
    }

    @Test
    fun executeNonSelectQuery() {
        //given
        val expectedNumberOfDeletions = em.createQuery(
            "SELECT COUNT (o) FROM Organization o WHERE o.usageCreditLimited = true"
        ).singleResult as Long

        //when
        val actualNumberOfDeletions = manipulate.execute {(
            deleteFrom(ORGANIZATION)
                .where(ORGANIZATION.USAGECREDITLIMITED.eq(true))
            ) as Query
        }

        //then
        assertThat(actualNumberOfDeletions).isEqualTo(expectedNumberOfDeletions)

    }
}

因为这个代码无法编译。

Error:(50, 50) Kotlin: Type inference failed: fun <T : Record> execute(query: DSLContext.() -> Select<T>): Result<T>
cannot be applied to
(DSLContext.() -> DeleteConditionStep<OrganizationRecord!>!)
Error:(50, 58) Kotlin: Type mismatch: inferred type is DSLContext.() -> DeleteConditionStep<OrganizationRecord!>! but DSLContext.() -> Select<???> was expected

别开玩笑,我不是要你使用那种方法,我是想让你用另一种。

我们来明确一下:

//when
val actualNumberOfDeletions = manipulate.execute {(
    deleteFrom(ORGANIZATION)
        .where(ORGANIZATION.USAGECREDITLIMITED.eq(true))
    ) as Query
}

仍然无法编译第二个测试用例,因为

Error:(50, 50) Kotlin: Type inference failed: fun <T : Record> execute(query: DSLContext.() -> Select<T>): Result<T>
cannot be applied to
(DSLContext.() -> Query)
Error:(50, 58) Kotlin: Type mismatch: inferred type is DSLContext.() -> Query but DSLContext.() -> Select<???> was expected

我该如何让Kotlin调用正确的方法?


Select 本质上是一个 Query,所以你想使用的方法无法正确解析。尝试更改方法签名,使其不依赖于传递的参数来解析该方法。 - Keivan Esbati
@KeivanEsbati,你能详细说明一下“所以你想使用的方法无法正确解析”吗?我可以看到确实是这种情况,但我更愿意知道为什么会出现这种情况。即使 Select 是一个 Query,反过来却不成立。那么为什么非 Select 查询不能与非 Select 方法绑定呢? - User1291
我添加了一个参考链接并将其作为答案发布,如果您愿意,可以阅读更多相关信息。 - Keivan Esbati
2
jOOQ区分fetch(生成结果)和execute(生成更新计数)。为什么要同时使用相同的术语呢?在某些情况下,重载比方便更容易出现问题...请注意,您可能希望引用ResultQuery<T>而不是Select<T>以获得更通用的解决方案。 - Lukas Eder
如果将您的代码提取到自己的函数中,例如 fun q() : DSLContext.() -> Query = { ... }fun s() : DSLContext.() -> Select 并将这些函数传递给您的 execute,它可能会(尚未测试)... 甚至以下内容也可能有效:fun <T> dsl(dsl : DSLContext.() -> T) = dsl 并调用 manipulate.execute(dsl { ... })... 它能工作吗?;-) 也许您迟早会在这里结束:Kotlin 中的类型擦除是如何工作的? 或者在这里:Kotlin 类型擦除 - ... 泛型 - Roland
1个回答

0

Select 本质上是 Query,因此您想要使用的方法可能无法正确推断。

编译器在方法调用方面解决了所有问题,而不注意泛型约束,直到最后一刻才会注意到所选择的方法无效,并失败并出现错误。

这个问题发生在使用泛型时,编译器将这些方法识别为两种不同的方法,但在尝试解析方法时,根据可用数据可能找不到正确的方法。如果您想知道原因,请查看 Jon Skeet 的文章 Overloading and Generic constraints


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接