【问题标题】:From kotlin to Java, issue on kotlin generics从 kotlin 到 Java,关于 kotlin 泛型的问题
【发布时间】:2021-03-15 10:42:29
【问题描述】:

我正在将代码从 java 迁移到 kotlin,但泛型存在一些问题。

这是一个解释我的疑惑的例子:

import java.util.*
import kotlin.collections.HashMap

interface MyGenerics<T>{
  fun produce():T
  fun publish(toPublish:T):String
}

class MyImplementation:MyGenerics<Boolean>{

  override fun produce(): Boolean {
    return true
  }

  override fun publish(toPublish: Boolean): String {
    return "test"
  }

}

class RegistrationClass{

  private val workers = HashMap<String, Vector<MyGenerics<Any>>>()

  fun register(type: String, worker: MyGenerics<Any>) {
    if (!workers.containsKey(type)) workers[type] = Vector()
      workers[type]!!.add(worker)


   //usage example
   for (workers in workers[type]!!){
     val product = workers.produce()
     workers.publish(product)
    }
  }

}

fun main() {
   val registrationClass = RegistrationClass()
   registrationClass.register("MyTest", MyImplementation()) //error kotlin implementation

   val javaRegistrationClass = JavaRegistrationClass()
   javaRegistrationClass.register("MyNewTest", MyImplementation()) //java implementation

}

为什么我会出错

> registrationClass.register("MyTest", MyImplementation()) //error
> kotlin implementation

而不是:

javaRegistrationClass.register("MyNewTest", MyImplementation()) //java implementation

这是 JavaRestristrationClass:

class JavaRegistrationClass{

    private HashMap workers = new HashMap<String, Vector<MyOtherTest>>();

    public void register(String type, MyGenerics worker) {
        //do something
    }

}

我已经阅读了这个https://stackoverflow.com/a/65534086/3681634 的答案,但我仍然不明白

【问题讨论】:

  • 注册需要MyGenerics&lt;Any&gt;,你不能将MyGenerics&lt;Boolean&gt;传递给它。在 java 中,您使用的是 kotlin 不支持的原始类型。您的代码在 Java 中不是类型安全的,因此您需要更改一些内容以使其在 Kotlin 中工作。如果没有看到您的其余代码,很难就此提出建议。
  • 嗨!谢谢;)我已经用一个用法示例编辑了这个问题。像这样: /*用法示例*/ for (workers in workers[type]!!){ val product = workers.produce() workers.publish(product) }

标签: kotlin generics


【解决方案1】:

在 java 代码中,您使用原始类型 (MyGenerics),它是不安全的(因此未添加到 Kotlin)并且仍然存在于 java 中,只是为了与 Java 5 之前编写的代码向后兼容。

MyGenerics&lt;Any&gt; 你使用的类型不是它的等价物。 您需要提供公平的泛型类型MyGenerics&lt;T&gt;(并使您的方法泛型),或者使用MyGenerics&lt;*&gt; 类型。

由于您故意在一个通用集合中混合不同类型的元素,因此在所有情况下都不可避免地会出现未经检查的强制转换。

选项 1(使用MyGenerics&lt;T&gt;):

class RegistrationClass {
    private val workers = HashMap<String, Vector<MyGenerics<Any>>>()

    fun <T> register(type: String, worker: MyGenerics<T>) {
        val workersOfType = workers.getOrPut(type) { Vector() }
        @Suppress("UNCHECKED_CAST")
        workersOfType.add(worker as MyGenerics<Any>)

        //usage example
        workersOfType.forEach { it.publish(it.produce()) }
    }
}

选项 2(使用MyGenerics&lt;*&gt;):

class RegistrationClass {
    private val workers = HashMap<String, Vector<MyGenerics<Any>>>()

    fun register(type: String, worker: MyGenerics<*>) {
        val workersOfType = workers.getOrPut(type) { Vector() }
        @Suppress("UNCHECKED_CAST")
        workersOfType.add(worker as MyGenerics<Any>)

        //usage example
        workersOfType.forEach { it.publish(it.produce()) }
    }
}

您也可以更改workers 集合的类型(正如@Tenfour04 在 cmets 中建议的那样):

选项 1.1/2.1(使用 MyGenerics&lt;T&gt;/MyGenerics&lt;*&gt;Vector&lt;MyGenerics&lt;*&gt;&gt;):

class RegistrationClass {
    private val workers = HashMap<String, Vector<MyGenerics<*>>>()

    fun <T> register(type: String, worker: MyGenerics<T>) { 
//  fun register(type: String, worker: MyGenerics<*>) { //also possible
        val workersOfType = workers.getOrPut(type) { Vector() }
        workersOfType.add(worker)

        //usage example
        workersOfType.forEach {
            @Suppress("UNCHECKED_CAST")
            it as MyGenerics<Any>
            it.publish(it.produce())
        }
    }
}

更新

实际上,可以避免使用选项 1.1/2.1 进行强制转换,如果您可以隐藏操作,在接口内显示通用集合元素的类型(正如 @al3c 在 cmets 中建议的那样):

interface MyGenerics<T> {
    fun produce(): T
    fun publish(toPublish: T): String
    fun produceAndPublish() = publish(produce())
}

class RegistrationClass {
    private val workers = HashMap<String, Vector<MyGenerics<*>>>()

    fun <T> register(type: String, worker: MyGenerics<T>) { 
//  fun register(type: String, worker: MyGenerics<*>) { //also possible
        val workersOfType = workers.getOrPut(type) { Vector() }
        workersOfType.add(worker)

        //usage example
        workersOfType.forEach { it.produceAndPublish() }
    }
}

【讨论】:

  • 解释为什么这样做会改善这个答案。
  • 最好使用Vector&lt;MyGenerics&lt;*&gt;&gt;&gt; 来明确缺乏类型安全性,并迫使您考虑在检索项目时强制转换是否安全。它会更接近 Java 版本中的原始类型使用。
  • 谢谢!有没有办法不使用 unchecked_cast ?
  • @Tenfour04, MyGenerics&lt;*&gt; 仍然不等同于 java 原始类型。它相当于MyGenerics&lt;?&gt;。我相信这种类型并不意味着缺乏类型安全,而是(以及MyGenerics&lt;Any&gt;)简化为一些非常基本的泛型类型。区别在于MyGenerics&lt;*&gt; 是各种MyGenerics&lt;...&gt; 的超类型,而MyGenerics&lt;Any&gt; 不是。因此,这意味着对尊重的通用集合的添加/检索操作有不同的限制。由于在这种情况下这两种操作都有,它只是将未经检查的强制转换移动到其他地方,保持语义相同。
  • 受@Tenfour04 启发,用更全面的理由和替代方案更新了我的答案
【解决方案2】:

我是这样做的:

class RegistrationClass{

  private val bookOrder = HashMap<String, Vector<MyGenerics<*>>>()

  fun register(type: String, worker: MyGenerics<*>) {
    if (!bookOrder.containsKey(type)) bookOrder[type] = Vector()
    bookOrder[type]!!.add(worker)
  }

  fun runExample(type:String){
    //usage example
    for (workers in bookOrder[type]!!){
      val product = workers.produce()
      @Suppress("UNCHECKED_CAST")
      (workers as MyGenerics<Any?>).publish(product)
    }
  }

}

fun main() {
  val registrationClass = RegistrationClass()
  registrationClass.register("MyTest", MyImplementation())
}

但我想知道: 有没有办法不使用未经检查的演员表?

【讨论】:

  • 您的代码根本上是不安全的,HashMap 无法知道它所拥有的类型,因为它们都是不同的类。最好的办法是完全从接口中删除参数,并提供一个方法来执行worker.publish(worker.produce())
  • 我创建了接口 MyGenerics 来强制方法“produce”的输出与方法“publish”的输入类型相同。是不是走错路了?
  • 如果你把它们都放在这样的地图上,你什么也得不到。 Java 无论如何都不知道什么类型,Java 没有明确的警告,但从根本上说,您也在进行未经检查的强制转换。例如,您可以在一个工人上调用生产并将其输出传递给任何其他工人,Java 不会阻止您。如果您只是将产生结果传递给发布,那么您不需要泛型类型。
  • 这是我真实代码的简化,这个 RegistrationClass 只能接受扩展 MyGenerycs 的类。这个 RegistrationClass 唯一要做的事情就是在后台执行 producer 方法并将结果发布到主线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多