【问题标题】:Why is Kotlins type inference failing when Javas isn't?为什么 Kotlin 类型推断失败,而 Java 没有?
【发布时间】:2019-12-10 12:05:17
【问题描述】:

鉴于以下代码(使用虚拟返回但显示了问题):

import com.github.roookeee.datus.api.Datus
import com.github.roookeee.datus.api.Mapper
import com.github.roookeee.datus.immutable.ConstructorParameter

data class EntryDto(val id: Long?, val title: String, val content: String)
data class EntryEntity(val id: Long? = null, val title: String, val content: String) {
    fun toDto(): EntryDto {
        val mapper: Mapper<EntryEntity, EntryDto> = Datus.forTypes(this.javaClass, EntryDto::class.java)
                .immutable(::EntryDto)
                .from(EntryEntity::id).to(ConstructorParameter::bind)
                .from(EntryEntity::title).to(ConstructorParameter::bind)
                .from(EntryEntity::content).to(ConstructorParameter::bind)
                .build()
        return EntryDto(null, "", "")
    }
}

Kotlin 无法推断出正确的泛型类型,而 Java >= 8 则可以(假设两个 Java 类与此处的数据类相同 - 两个不可变对象类)。我尝试使用 Kotlin 1.3.0 和 -XXLanguage:+NewInference 的默认值,但后者甚至无法推断为 .immutable 选择正确的重载。

这是使上述代码编译的数据依赖信息(没有库我无法减少这个问题,它的通用用法太复杂了):

<dependency>
    <groupId>com.github.roookeee</groupId>
    <artifactId>datus</artifactId>
    <version>1.3.0</version>
</dependency>

我错过了什么吗?我很想让我的库与 kotlin 更兼容,但不知道如何从这里开始,或者推理错误的确切名称是什么。

您可以找到数据源here

这是对应的java代码:

import com.github.roookeee.datus.api.Datus;
import com.github.roookeee.datus.api.Mapper;
import com.github.roookeee.datus.immutable.ConstructorParameter;

class Java8Code {
    static class EntryDto {
        private final Long id;
        private final String title;
        private final String content;

        EntryDto(Long id, String title, String content) {
            this.id = id;
            this.title = title;
            this.content = content;
        }

        public Long getId() {
            return id;
        }

        public String getTitle() {
            return title;
        }

        public String getContent() {
            return content;
        }
    }

    static class EntryEntity {
        private final Long id;
        private final String title;
        private final String content;

        EntryEntity(Long id, String title, String content) {
            this.id = id;
            this.title = title;
            this.content = content;
        }

        public Long getId() {
            return id;
        }

        public String getTitle() {
            return title;
        }

        public String getContent() {
            return content;
        }

        public EntryDto toDto() {
            Mapper<EntryEntity, EntryDto> mapper = Datus.forTypes(EntryEntity.class, EntryDto.class)
                    .immutable(EntryDto::new)
                    .from(EntryEntity::getId).to(ConstructorParameter::bind)
                    .from(EntryEntity::getTitle).to(ConstructorParameter::bind)
                    .from(EntryEntity::getContent).to(ConstructorParameter::bind)
                    .build();
            return mapper.convert(this);
        }
    }

}

编辑 2:错误消息的图像 + 下面的一些注释

3 type arguments expected for interface ConstructorParameter&lt;In : Any!, GetterReturnType : Any!, Result : Any!&gt; - Kotlin 似乎期望接口方法引用的泛型类型参数,但这在 Kotlin 中是不可能的,在 Java 中也不需要。

【问题讨论】:

  • 您能否提供行为符合您预期的 Java 8 代码示例?
  • 我会说我相信这与 Kotlin 的 to() 扩展功能有关。
  • 我已经完成了您的要求 :) 我不需要导入 to() 来成为这里的问题吗?我的 IDE 抱怨没有足够的类型信息来推断,所以我不认为它与您的链接扩展功能有关
  • 尝试用 lambda 而不是方法引用替换 to 调用:(to { param, getter -&gt; param.bind(getter) },我猜?有一个 JavaDoc 来查看类型会有所帮助。)我以前看到它有助于类型参数推断.
  • 是的,但它确实很麻烦:(我想我可以为 kotlin 添加一些类型提示(例如注释)。你知道吗?主要问题仍然存在:Kotlins 类型推断是在这里失败

标签: java generics kotlin type-inference


【解决方案1】:

to 扩展功能不是问题。它只是弹出,因为编译器没有看到成员方法的正确参数。

Kotlin 不喜欢的是没有附加类型参数的泛型类型。在您指定类型参数之前,ConstructorParameter 不是类型。所以当它看到ConstructorParameter::bind时会报错,::的左边应该是一个类型。

如果你写.to { x, y -&gt; x.bind(y) },Kotlin 可以很好地推断类型。 但是你不能指望你的用户多次编写这个“相同”的 lambda。

扩展功能来救援!

fun <In, CurrentType, Next> ConstructorParameterBinding<In, CurrentType, out ConstructorParameter<In, CurrentType, Next>>.bind(): Next =
    this.to { x, y -> x.bind(y) }

val mapper: Mapper<EntryEntity, EntryDto> = Datus.forTypes(this.javaClass, EntryDto::class.java)
    .immutable(::EntryDto)
    .from(EntryEntity::id).bind()
    .from(EntryEntity::title).bind()
    .from(EntryEntity::content).bind()
    .build()

【讨论】:

  • 感谢您的想法,也尝试了扩展功能,所以我已经知道这个了。这种方法有两个(主观的、与库相关的)问题:1)我需要发布第二个工件以支持 kotlin 2)from().map().to() 的一般流程被破坏,我努力统一可变和不可变 API的数据。关于如何保持上述流程的任何想法?如果可能的话,我希望 Java 和 Kotlin 流程相同。很高兴在这里为文档提供这个答案:)
  • 我仍然感到困惑的是,像 Kotlin 这样更侧重于类型推理的语言在处理这个问题方面的表现不如 Java :o
猜你喜欢
  • 2018-08-21
  • 2020-12-30
  • 2018-05-15
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
相关资源
最近更新 更多