Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

instance::class.java vs. instance.javaClass

Given Kotlin 1.1. For an instance of some class, instance::class.java and instance.javaClass seem to be nearly equivalent:

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

There is a subtle difference, however:

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

instance.javaClass is negligibly shorter, but instance::class.java is more consistent with the corresponding usage on a type. While you can use .javaClass on some types, the result may not be what you would expect:

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

So, I would argue that it is better to never use .javaClass for more consistency. Are there any arguments against that?

like image 813
Marco Eckstein Avatar asked Oct 10 '17 19:10

Marco Eckstein


People also ask

What is the instance of a class in Java?

An instance of a class is an object. It is also known as a class object or class instance. As such, instantiation may be referred to as construction. Whenever values vary from one object to another, they are called instance variables. These variables are specific to a particular instance.

What is class object and instance in Java?

A class is a blueprint which you use to create objects. An object is an instance of a class - it's a concrete 'thing' that you made using a specific class. So, 'object' and 'instance' are the same thing, but the word 'instance' indicates the relationship of an object to its class.

What is instance in Java?

What is instance variable in Java? Instance variables in Java are non-static variables which are defined in a class outside any method, constructor or a block. Each instantiated object of the class has a separate copy or instance of that variable. An instance variable belongs to a class.


1 Answers

The difference in these two constructs is that, for an expression foo of static (declared or inferred) type Foo:

  • foo.javaClass is typed as Class<Foo>

  • foo::class.java is typed as Class<out Foo>

In fact, the latter is more precise, because the actual value that foo evaluates to can be an instance of not Foo itself but one of its subtypes (and it's exactly what's denoted by the covariant out Foo).

As @marstran correctly noted in the comment on the question, .javaClass once was considered to be deprecated (see the Kotlin 1.1 RC announcement) because it can break type safety (see below), but it was afterwards left as-is because it was widely used and replacing it with the alternative of ::class.java would require adding explicit unchecked casts in the code.

Also, see the comments under this answer: (link)


Please note that Int.javaClass does not denote the type of Int but instead is the Java class of the Int's companion object. Whereas Int::class.java is an unbound class reference and denotes the type. To get it with .javaClass, you need to call it on an Int instance, e.g. 1.javaClass.


Here's how exactly .javaClass can break type safety. This code compiles but breaks at runtime:

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)
}

This example uses kotlin-reflect.

That's because someFooProperty represents a property of Bar, not Foo, but since it was obtained from someFoo.javaClass (Class<Foo> then converted to KClass<Foo>) the compiler allows us to use it with the in Foo projection.

like image 123
hotkey Avatar answered Oct 10 '22 22:10

hotkey