12

我试过这个,代码没有编译。

class GenericClass<T>() {
    private var arr : Array<T>? = null

    {
        arr = Array<T>(10, { null })
    }
}
4

4 回答 4

19

这段代码报告了两个编译器错误:一个是关于可空类型的,另一个是关于泛型的。

可空类型。Kotlin 强制执行可空引用的规则,并且由于 T 可以用例如 String 使 arr 成为 Array 类型来实例化,因此编译器不允许您将空值放入该数组中。如果您想要空值,则必须将类型更改为 Array:

class GenericClass<T>() {
    private var arr : Array<T?>? = null

    {
        arr = Array(10, { null }) // No need to specify type arguments again
    }
}

泛型。上面的示例仍然存在编译时错误,因为我们正在尝试构造一个未知类型 T 的数组。请注意,这个问题在 Java 中也存在。Kotlin 被编译为 JVM 字节码需要两件事:

  • 泛型类型参数在运行时被擦除,
  • 除了数组的通用参数。

这意味着在字节码中,Kotlin 必须创建一个具体类型的数组,而不是未知类型 T。它可以在看到 Array 时创建 Objects 数组,但这不起作用,例如,在这种情况下:

fun test() {
    fun foo(srts: Array<String?>) {
        // ...
    }
    val gc = GenericClass<String>()
    foo(gc.arr)
}

在这里,在最后一行中,我们尝试将 Object[] 传递到预期 String[] 的位置,并得到一个运行时错误。

这就是 Kotlin 拒绝创建 T 数组的原因。您可以通过显式抑制类型系统来解决这个问题,即使用类型转换:

class GenericClass<T>() {
    val arr : Array<T?>

    {
        arr = Array<Any?>(10, { null }) as Array<T?>
    }
}

这里我们显式地请求创建一个 Any 数组(编译为 Object[]),然后将其类型转换为一个 T 数组。编译器发出警告,但服从我们的意愿。

请注意,上面有问题的示例仍然存在,即,如果您将这种方式创建的数组传递给预期的字符串数组,它将在运行时失败。

于 2013-11-30T06:40:48.980 回答
2

方法

val array : Array<T?> = kotlin.arrayOfNulls<T>(size)

来自文档

/**
*Returns an array of objects of the given type with the given [size],
*initialized with null values.
*/
public fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
于 2016-07-31T18:57:48.440 回答
1

如果需要在构造函数中初始化数组,可以添加内联工厂方法并使用reified T. 此解决方案的灵感来自答案https://stackoverflow.com/a/41946516/13044086

class GenericClass<T> protected constructor(
    private val arr : Array<T?>
) {
    companion object {
        inline fun <reified T>create(size: Int) = GenericClass<T>(arrayOfNulls(size))
    }
}

fun main() {
    val strs = GenericClass.create<String>(10)
    ...
}

注意构造函数是受保护的,因为内联函数不能访问私有构造函数。

如果需要在创建对象后创建数组,可以将创建数组的 lambda 传递给方法。Lambda 可以在扩展函数内部创建,因此保留了有关数组类型的信息。@PublishedApi注解用于封装私有方法fill

import GenericClass.Companion.fill

class GenericClass<T> {
    private var arr : Array<T?>? = null

    fun show() {
        print(arr?.contentToString())
    }

    private fun fill(arrayFactory: (size: Int) -> Array<T?>) {
        this.arr = arrayFactory(10)
    }

    @PublishedApi
    internal fun `access$fill`(arrayFactory: (size: Int) -> Array<T?>) = fill(arrayFactory)

    companion object {
        inline fun <reified T>GenericClass<T>.fill() {
            `access$fill`(arrayFactory = { size -> arrayOfNulls(size) })
        }
    }
}

fun main() {
    val strs = GenericClass<String>()
    strs.fill()
    strs.show()
}
于 2020-05-06T18:27:49.193 回答
0

您可以使用如下辅助函数:

@Suppress("UNCHECKED_CAST")
fun <T> genericArrayOfNulls(size: Int): Array<T?> {
    return arrayOfNulls<Any?>(size) as Array<T?>
}
于 2019-10-27T20:55:58.960 回答