类型推断失败:没有足够的信息来推断参数,请明确指定

7

我正在尝试使用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)
}

但它仍然导致与第一个相同的错误信息。
3个回答

3
我已经尝试了你的示例,在我的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 Community Edition。

然而,我建议您使用 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 上也没有任何问题。 - petey

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)

2
这个问题与Vaadin有关,和Android没有任何关系。 - Martin Vysny

0

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

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

2
那样做是可行的,但我想使用类型安全的数据绑定,并了解类型推断的问题所在。 - Jens Baitinger
1
这实际上是一个很好的建议,原因如下:如果您尝试使用BeanValidationBinder和JSR-303验证以bind(ValueProvider,Setter)方法进行绑定,它将跳过JSR-303验证!您实际上需要使用bind(String),以便可以从字段本身中提取验证注释。 - Martin Vysny
@MartinVysny 我同意。然而,OP想要用Kotlin的方式来做。我能理解这一点。 - petey
@petey 我同意。这就是为什么我不直接使用“bind(String)”;而是使用上述的“bind(KMutableProperty1)”。 - Martin Vysny

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