What is the difference between Int::class.java and Int::class.javaObjectType, and when does it bite you?
answer
- .java -> primitive int; .javaObjectType -> Integer
- int.class != Integer.class (distinct Class objects)
- Generics/erasure force the wrapper -> javaObjectType
- Wrong one -> NoSuchMethodException on ctor/method lookup
- String: both return the same Class
basics
~10 sFor number-like types, .java may give the primitive class (like int) while .javaObjectType gives the boxed wrapper (like Integer). Some Java APIs only accept the wrapper, so the wrong one fails.
solid answer
~40 sFor Kotlin types that map to JVM primitives (Int, Long, Boolean, Double, Char, etc.), `KClass.java` can return the **primitive** `Class` (e.g. `int`, `boolean`), whereas `KClass.javaObjectType` always returns the **boxed wrapper** `Class` (`java.lang.Integer`, `java.lang.Boolean`). They are different `java.lang.Class` objects: the primitive `int` class versus the `java.lang.Integer` class. This bites when a reflective/generic API works only with reference types — e.g. building a `List<Class<*>>` of generic arguments, calling `Method.invoke` with boxed args, looking up a constructor by parameter types, or registering types in a framework that keys on the wrapper. Passing the primitive `Class` where a wrapper is expected (or vice versa) yields `NoSuchMethodException` or type-mismatch errors. Rule of thumb: use `javaObjectType` whenever the type might be used as a generic type argument or boxed value.
code
kotlin · 8 linesprintln(Int::class.java) // int
println(Int::class.javaObjectType) // class java.lang.Integer
println(Int::class.java == Int::class.javaObjectType) // false
// Method lookup is signature-sensitive:
class Box { fun setPrim(x: Int) {}; fun setBoxed(x: Integer) {} }
Box::class.java.getMethod("setPrim", Int::class.java) // matches (int)
Box::class.java.getMethod("setBoxed", Int::class.javaObjectType) // matches (Integer)go deeper
Aware that Int can be a primitive in Java and that there are two properties, even if details are fuzzy.
Knows .java may give int while .javaObjectType gives Integer and that they're different Class objects.
Predicts NoSuchMethodException from picking the wrong one in constructor/method lookup and uses javaObjectType for generic arguments.
Reasons about erasure-driven boxing across reflective dispatch, arrays (int[] vs Integer[]), and framework type registries, choosing the correct handle per call site.
## Primitives vs wrappers on the JVM The JVM has primitive types (`int`, `long`, `boolean`, `char`, ...) that are **not** objects, and corresponding **boxed wrapper** classes (`java.lang.Integer`, `java.lang.Long`, ...) that are objects. Each has its own distinct `java.lang.Class` instance — `int.class` is **not** `Integer.class`. Kotlin hides this: you write `Int` everywhere and the compiler picks primitive `int` or boxed `Integer` depending on context (nullable `Int?`, generics, and collections force boxing). ## Two reflective views of the same Kotlin type ```kotlin Int::class.java // int (primitive Class) Int::class.javaObjectType // class java.lang.Integer (boxed Class) Int::class.java == Int::class.javaObjectType // false ``` - `KClass<T>.java` — returns the **primitive** Class for primitive-mapped types, otherwise the normal Class. - `KClass<T>.javaObjectType` — **always** returns the **boxed/reference** Class. For a non-primitive type like `String`, both return the same `java.lang.Class` (`String` has no primitive form). ## When the distinction bites 1. **Constructor/method lookup by parameter types.** `clazz.getConstructor(Int::class.java)` looks for `(int)`, while `getConstructor(Int::class.javaObjectType)` looks for `(Integer)`. Pick the wrong one and you get `NoSuchMethodException`. 2. **Generic type arguments.** Generics are always reference types (erasure boxes primitives). A `Class<*>` used as a generic argument must be the **wrapper**, so use `javaObjectType`. 3. **Method.invoke / arrays of types.** When assembling `arrayOf(Int::class.java, ...)` for reflective dispatch, mixing primitive and boxed Class objects can mismatch the actual method descriptor. 4. **Framework registries / type maps** that key on the wrapper class will miss entries keyed under the primitive. ## Practical rule ```kotlin // Looking up a primitive-parameter method: val m = cls.getMethod("setX", Int::class.java) // (int) // Building a generic/boxed type list: val types: List<Class<*>> = listOf(Int::class.javaObjectType) // Integer ``` Use `.java` when you genuinely target a primitive signature; use `.javaObjectType` whenever the value will be boxed or used as a generic argument. The same idea extends to arrays: `IntArray::class.java` is `int[]`, distinct from `Array<Int>::class.java` which is `Integer[]`.
- What does String::class.java vs String::class.javaObjectType return?Both return the same java.lang.Class<String>; String has no primitive form, so there's nothing to box/unbox.
- Why must a generic type argument use javaObjectType?Generics are erased to reference types, so a Class used as a type argument has to be the boxed wrapper, not the primitive.
Same Kotlin Int, two ID cards: .java is the lean primitive badge, .javaObjectType is the boxed-object badge.
saying these in an interview costs you the question
- Assuming Int::class.java is always Integer
- Believing int.class equals Integer.class
- Using .java for a generic type-argument list
- Not connecting NoSuchMethodException to primitive/boxed mismatch
- Thinking javaObjectType matters for String