How do kotlin.String and kotlin.Any map onto Java types, and what is surprising about the String mapping given Kotlin's null-safety?
answer
- String IS java.lang.String — same class, no conversion
- Any ↔ Object; Any? is the real top type
- Nullability has no bytecode representation
- Java returns surface as platform types (String!)
- Annotations (@Nullable/@NotNull/JSpecify) restore precision
basics
~20 skotlin.String is literally java.lang.String at runtime — same class, same methods. kotlin.Any maps to java.lang.Object. The surprise is that the same Java String class can appear in Kotlin as either non-null String or nullable String?, because Java doesn't track nullability.
solid answer
~40 skotlin.String and java.lang.String are the same runtime class — String is a mapped type, so there's no separate kotlin/String. Kotlin adds compile-time extensions and enforces non-nullability, but the underlying object is a java.lang.String. kotlin.Any maps to java.lang.Object, and Any? is the true top type that also includes null. The non-obvious part: nullability is a Kotlin compiler concept with no JVM representation. So a Java method returning String surfaces in Kotlin as a 'platform type' String! that you may treat as String or String?. The compiler can't prove it's non-null, so the safety guarantee is yours to assert. Any methods (toString/equals/hashCode) come straight from Object.
code
kotlin · 8 lines// All true at runtime
val s: String = "hi"
val asObject: Any = s // Any == Object
println(s is java.lang.String) // true — same class
// Platform type from unannotated Java
// String getValue(); -> val v: String! = obj.value
val forced: String = obj.value // asserts non-null; NPE if it was nullgo deeper
Knows String is Java's String and Any is Object; can call Java string methods.
Explains platform types and why nullability is unenforced at the Java boundary; distinguishes Any from Any?.
Designs interop boundaries that annotate Java or wrap calls to convert platform types into explicit String?/String early.
Drives team conventions (JSpecify adoption, null-checks at boundaries) to eliminate platform-type NPEs across a large mixed codebase.
## String is java.lang.String `kotlin.String` is a **mapped type**: at runtime it *is* `java.lang.String`. There is no separate Kotlin string class. What Kotlin adds is purely at compile time: - extension functions and properties (`length` as a property, `substring`, `first`, etc.) - the non-null guarantee enforced by the type checker A `kotlin.String` value can be passed directly to any Java method expecting `java.lang.String` and vice versa — zero conversion, zero wrapping. ## Any is java.lang.Object `kotlin.Any` maps to `java.lang.Object`. Its three members — `equals()`, `hashCode()`, `toString()` — are the Object methods. But there's a subtlety: **`Any` is non-null**, while the real top type is **`Any?`**, which includes `null`. Java's `Object` reference can be null, so a Java method returning `Object` shows up in Kotlin as the platform type `Any!`. ## The nullability surprise: platform types Nullability (`String` vs `String?`) is a **Kotlin-only** notion with **no bytecode representation**. The JVM has just one `java.lang.String`. So when you call Java that returns a `String`, Kotlin can't know if it's nullable. It assigns a **platform type**, written `String!`, meaning 'could be `String` or `String?` — you decide': ```kotlin // Java: public String getName() { ... } val n = javaObj.name // type is String! (platform type) val a: String = javaObj.name // you assert non-null val b: String? = javaObj.name // you treat as nullable val len = javaObj.name.length // risks NPE if it was actually null ``` If the Java side is annotated (`@Nullable`/`@NotNull`, JSpecify, etc.), Kotlin honors it and gives you a precise `String?`/`String` instead of a platform type. ## Why this matters - You get seamless interop (same String/Object classes) **but** you inherit responsibility for null-correctness at the boundary. - A wrongly-asserted platform type is the classic source of an interop `NullPointerException`. ## Quick map - `kotlin.String` ↔ `java.lang.String` - `kotlin.CharSequence` ↔ `java.lang.CharSequence` - `kotlin.Any` ↔ `java.lang.Object` (and `Any?` is the genuine top type)
- What is a platform type and how is it written?A type coming from unannotated Java whose nullability is unknown to Kotlin, written like String! (you can't write it yourself). You choose to treat it as String or String?, and bear the NPE risk.
- If Any maps to Object, why does Kotlin still have Any? as a distinct type?Any is non-null; the genuine top type that admits null is Any?. Both erase to Object on the JVM, but the Kotlin type system distinguishes them for null-safety.
Kotlin's String is the same physical object as Java's String wearing a 'guaranteed non-null' badge that only Kotlin's compiler can read — the JVM sees just the object.
saying these in an interview costs you the question
- Saying Kotlin has its own String class separate from Java's
- Claiming nullability is stored in the bytecode
- Treating every Java-returned String as guaranteed non-null
- Saying Any maps to Object including null (that's Any?)
- Believing String→java.lang.String requires a conversion call