鉴于Kotlin 1.1。对于某个班级的instance
,instance::class.java
和instance.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
来提高一致性。有没有反对的论据?
答案 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>
投影一起使用。