【问题标题】:Type inference failed: Not enough information to infer parameter Please specify it explicitly类型推断失败:没有足够的信息来推断参数请明确指定
【发布时间】:2017-06-15 17:04:52
【问题描述】:

我正在尝试在 Kotlin 中编写 Vaadin 应用程序。对于数据绑定,Vaadin 8 现在提供了类型安全数据绑定的可能性。在 Kotlin 中,我会期待这样的工作:

class LoginModel {
    var username: String = ""
    var password: String = ""
}

class LoginView : FormLayout() {
  val name = TextField("name")
  val password = TextField("password")
  val loginButton = Button("login")

  init {
      val binder = Binder<LoginModel>()
      binder.forField(name).bind(
          { it.username }, 
          { bean, value -> bean.username = value })

     //... 
  }
}

我在这里收到以下错误消息:

Error:(23, 31) Kotlin: Type inference failed: Not enough information to infer parameter BEAN in fun <BEAN : Any!, TARGET : Any!, BEAN : Any!> Binder.BindingBuilder<BEAN#1 (type parameter of bind), TARGET>.bind(p0: ((BEAN#1!) -> TARGET!)!, p1: ((BEAN#1!, TARGET!) -> Unit)!): Binder.Binding<BEAN#1!, TARGET!>!
Please specify it explicitly.

我尝试通过显式指定类型参数:

binder.forField(name).bind<LoginView, String, LoginView>(
    { it.username }, 
    { bean, value -> bean.username = value })

但这会导致错误消息(和其他语法错误,所以我没有遵循那种方法)

Error:(23, 35) Kotlin: No type arguments expected for fun bind(p0: ValueProvider<LoginModel!, String!>!, p1: Setter<LoginModel!, String!>!): Binder.Binding<LoginModel!, String!>! defined in com.vaadin.data.Binder.BindingBuilder

我的第二种方法是尝试直接传递 Kotlin 属性访问器,但错误消息与第一种相同:

binder.forField(name).bind(LoginModel::username.getter, LoginModel::username.setter)

最后一种方法是尝试使用扩展方法并使所有内容尽可能明确:

fun <BEAN, TARGET> Binder.BindingBuilder<BEAN, TARGET>.bind(property: KMutableProperty1<BEAN, TARGET>) {

    fun set(bean: BEAN): TARGET = property.get(bean)
    fun get(bean: BEAN, value: TARGET): Unit = property.set(bean, value)
    this.bind(::set, ::get)
}

但它仍然会导致与第一个相同的错误消息

【问题讨论】:

    标签: data-binding vaadin kotlin type-inference


    【解决方案1】:

    我已经尝试了您的示例,它可以与我的 Intellij 2017.1.4 和 Kotlin 1.1.2-5 一起编译。可能您在旧版本的 Kotlin 插件中发现了一个错误?

    bind 方法不接受泛型参数,因此无法泛化。 forField 方法接受一个泛型参数,或许你可以试试

        binder.forField<String>(name).bind(
                { it.username },
                { bean, value -> bean.username = value })
    

    但首先,请确保您拥有最新版本的 Kotlin 插件,并且/或者可以使用 Intellij 社区版试用。

    但是,我强烈建议您使用bind(String),因为其他绑定方法不适用于 JSR303 验证。还可以定义如下扩展方法:

    fun <BEAN, FIELDVALUE> Binder.BindingBuilder<BEAN, FIELDVALUE>.bind(prop: KMutableProperty1<BEAN, FIELDVALUE?>): Binder.Binding<BEAN, FIELDVALUE> =
            bind(prop.name)
    

    并从您的代码binder.forField(name).bind(LoginModel::username) 中按如下方式调用它。更多示例请参见此处:https://github.com/mvysny/karibu-dsl/blob/master/example-v8/src/main/kotlin/com/github/vok/karibudsl/example/form/FormView.kt

    【讨论】:

    • 我在 Kotlin 1.1.2-5 上也没有任何问题。
    【解决方案2】:

    您必须使用 API 级别 26+。此版本更改了 View.findViewById() 的签名 - 请参阅此处https://developer.android.com/preview/api-overview.html#fvbi-signature

    findViewById的结果不明确,需要提供类型:

    例如:

    val myTextView = findViewById(R.id.list) as TextView
    

    变成

    val myTextView = findViewById<TextView>(R.id.myTextView)
    

    【讨论】:

    • 这个问题和Vaadin有关,和Android完全没有关系。
    【解决方案3】:

    由于您的模型相当简单,您可以通过Binder#bind(String propertyName) 方法连接到具有给定名称的属性来完成绑定

    val binder = Binder<LoginModel>()
    binder.forField(name).bind("username");
    

    【讨论】:

    • 那行得通,但我想使用类型安全的数据绑定并了解类型推断的问题到底是什么。
    • 这实际上是一个很好的建议,原因如下:如果您曾经尝试通过 bind(ValueProvider, Setter) 方法将 BeanValidationBinder 与 JSR-303 验证一起使用,它将跳过 JSR-303 验证!您实际上需要使用 bind(String) 以便可以从字段本身中选择验证注释。
    • @MartinVysny 我同意。但是,OP 想要使用 kotlin 风格。我很感激。
    • @petey 我同意。这就是我不直接使用bind(String) 的原因;但我使用的是上述bind(KMutableProperty1)
    猜你喜欢
    • 1970-01-01
    • 2020-03-11
    • 2021-02-11
    • 2020-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-29
    • 1970-01-01
    相关资源
    最近更新 更多