instance :: class.java与instance.javaClass

时间:2017-10-10 19:48:08

标签: reflection kotlin

鉴于Kotlin 1.1。对于某个班级的instanceinstance::class.javainstance.javaClass似乎几乎相同:

val i = 0
println(i::class.java) // int
println(i.javaClass) // int
println(i::class.java === i.javaClass) // true

然而,有一个微妙的区别:

val c1: Class<out Int> = i::class.java
val c2: Class<Int> = i.javaClass

instance.javaClass可以忽略不计,但instance::class.java与类型的相应用法更加一致。虽然您可以在某些类型上使用.javaClass,但结果可能不是您所期望的结果:

println(i::class.java === Int::class.java) // true
println(i.javaClass === Int.javaClass) // false
println(Int::class.java === Int.javaClass) // false
println(Int.javaClass) // class kotlin.jvm.internal.IntCompanionObject

所以,我认为最好永远不要使用.javaClass来提高一致性。有没有反对的论据?

1 个答案:

答案 0 :(得分:11)

这两个结构的区别在于,对于静态(声明或推断)类型foo的表达式Foo

  • foo.javaClass输入为Class<Foo>

  • foo::class.java输入为Class<out Foo>

实际上,后者更精确,因为foo求值的实际值可以是非Foo本身的实例,而是其子类型之一(并且它正是协变所表示的内容) out Foo)。

正如@marstran在对该问题的评论中正确指出的那样,.javaClass曾被认为已被弃用(请参阅Kotlin 1.1 RC announcement),因为它可能会破坏类型安全性(见下文),但之后保持原样,因为它被广泛使用,并用替代的::class.java替换它将需要在代码中添加明确的未经检查的强制转换。

此外,请参阅此答案下的评论:(link)

请注意,Int.javaClass不表示Int的类型,而是Int的伴随对象的Java类。而Int::class.java是未绑定的类引用,表示类型。要通过.javaClass获取,您需要在Int个实例上调用它,例如1.javaClass

以下是.javaClass确切打破类型安全的方式。此代码在运行时编译但中断:

open class Foo

class Bar : Foo() {
    val baz: Int = 0
}

fun main(args: Array<String>) {
    val someFoo: Foo = Bar()
    val anotherFoo: Foo = Foo()

    val someFooProperty: KProperty1<in Foo, *> = // 'in Foo' is bad
            someFoo.javaClass.kotlin.memberProperties.first()

    val someValue = someFooProperty.get(anotherFoo)
}

此示例使用kotlin-reflect

这是因为someFooProperty表示Bar的属性,而不是Foo,但是因为它是从someFoo.javaClass获得的(Class<Foo>然后转换为{{1}编译器允许我们将它与KClass<Foo>投影一起使用。