instance :: class.java与instance.javaClass

Mar*_*ein 15 reflection kotlin

鉴于Kotlin 1.1.对于instance一些类,instance::class.javainstance.javaClass似乎是等效的:

val i = 0
println(i::class.java) // int
println(i.javaClass) // int
println(i::class.java === i.javaClass) // true
Run Code Online (Sandbox Code Playgroud)

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

val c1: Class<out Int> = i::class.java
val c2: Class<Int> = i.javaClass
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

hot*_*key 12

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

  • foo.javaClass 输入为 Class<Foo>

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

事实上,后者更精确,因为foo评估的实际值可以是不是Foo自身的实例,而是其子类型之一(并且它正是协变所表示的out Foo).

正如@marstran在该问题的评论中正确指出的那样,.javaClass曾经被认为是被弃用的(参见Kotlin 1.1 RC公告),因为它可以打破类型安全(见下文),但之后因为被广泛使用而被保留了原样.并用替代方法替换::class.java它将需要在代码中添加显式未经检查的强制转换.

另外,请参阅此答案下的评论:( 链接)


请注意,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)
}
Run Code Online (Sandbox Code Playgroud)

这个例子使用kotlin-reflect.

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