404 Not Found

404 Not Found


nginx

Kotlin Variables and Type System In-Depth

Kotlin's type system is not Java's clone — it eliminates forced casts with smart casts, builds a complete type algebra with Any/Unit/Nothing, and makes the type system work for you instead of against you.

1. What You'll Learn


2. A Developer's Real Story

(1) Pain Point: Java's Type-Casting Hell

Bob was maintaining Java code for OrderProcessor, and forced casts were everywhere: (Order), (String). Missing a single instanceof check triggered a ClassCastException — 3-5 production incidents per quarter.

(2) Kotlin's Smart Cast Solution

KOTLIN
// Java: verbose and error-prone
if (obj instanceof Order) {
    Order order = (Order) obj;  // Must cast again
    System.out.println(order.getId());
}

// Kotlin: smart cast eliminates the cast
if (obj is Order) {
    println(obj.id)  // No cast needed!
}

The Kotlin compiler auto-inserts safe casts after is checks. Bob never wrote a manual cast again.


3. Basic Types

(1) Numeric Types

Kotlin doesn't expose Java's primitive types (int/double) — everything is an object — but the compiler optimizes them to primitives on the JVM.

KOTLIN
val intValue: Int = 42          // 32-bit
val longValue: Long = 42L       // 64-bit
val doubleValue: Double = 3.14  // 64-bit IEEE 754
val floatValue: Float = 3.14f   // 32-bit IEEE 754

// Underscore for readability
val million = 1_000_000
val creditCard = 1234_5678_9012_3456L
val bytes = 0b11010010_01101001
Type Bits Range Java Equivalent
Byte 8 -128 ~ 127 byte
Short 16 -32768 ~ 32767 short
Int 32 -2³¹ ~ 2³¹-1 int
Long 64 -2⁶³ ~ 2⁶³-1 long
Float 32 IEEE 754 float
Double 64 IEEE 754 double

(2) Non-Numeric Types

KOTLIN
val bool: Boolean = true
val char: Char = 'A'
val str: String = "OrderProcessor"

// Character is NOT a number in Kotlin
// val code: Int = 'A'  // ERROR
val code: Int = 'A'.code  // OK: 65
val charFromCode: Char = 65.toChar()  // OK: 'A'

4. Smart Cast

The Kotlin compiler automatically casts a variable to a more specific type after a type check — no manual casting required.

(1) Basic Smart Cast

KOTLIN
fun process(input: Any) {
    // After 'is' check, compiler smart casts
    if (input is String) {
        println(input.length)  // Smart cast to String
    }

    // After '!is' check
    if (input !is String) return
    println(input.length)  // Smart cast to String
}

// Smart cast with when
fun describe(obj: Any): String = when (obj) {
    is String -> "String: ${obj.length} chars"     // Smart cast
    is Int -> "Int: ${obj.dec()}"                   // Smart cast
    is List<*> -> "List: ${obj.size} items"         // Smart cast
    else -> "Unknown"
}

(2) Smart Cast Limitations

KOTLIN
// WARNING: smart cast may not work with var or custom getters
var obj: Any = "Hello"
if (obj is String) {
    // obj could be changed between check and use
    // println(obj.length)  // WARNING in some cases
}

// Safe approach: use local val
val safeObj = obj
if (safeObj is String) {
    println(safeObj.length)  // OK - guaranteed stable
}

(3) Java Cast vs Kotlin Smart Cast

Dimension Java Kotlin
Syntax (Type)obj Auto
Safety May cause CCE at runtime Compile-time guarantee
Code size Check + cast (two steps) Check = cast (one step)
Null safety May cause NPE Auto handles nullables

5. Explicit Type Conversions

Kotlin does not support implicit narrowing conversions (e.g., LongInt) — all conversions must be explicitly called.

(1) Conversion Functions

KOTLIN
val longVal: Long = 42L
val intVal: Int = longVal.toInt()    // Explicit narrowing
val doubleVal: Double = 3.99
val intFromDouble: Int = doubleVal.toInt()  // Truncates: 3

// String to number
val parsed: Int = "42".toInt()
val parsedDouble: Double = "3.14".toDouble()

// Number to string
val strVal: String = 42.toString()

(2) Why No Implicit Conversions?

KOTLIN
// This would silently lose data - Kotlin prevents it
val longValue: Long = 2_147_483_648L  // Exceeds Int.MAX_VALUE
// val intValue: Int = longValue  // COMPILE ERROR

// Must be explicit about potential data loss
val intValue: Int = longValue.toInt()  // OK but may overflow
Conversion Type Java Kotlin
Widening (Int→Long) Implicit Explicit toLong()
Narrowing (Long→Int) Implicit (risky) Explicit toInt()
String→Number Integer.parseInt() "42".toInt()
Number→String "" + 42 42.toString()

6. Three Top-Level Types

(1) Type Hierarchy Diagram

100%
classDiagram
    Any --> String
    Any --> Int
    Any --> Order
    Any --> Unit
    Any --> Nothing
    class Any {
        +equals()
        +hashCode()
        +toString()
    }
    class Unit {
        <<singleton>>
    }
    class Nothing {
        <<never returns>>
    }

(2) Any: The Parent of All Non-Null Types

KOTLIN
// Any is the root of the Kotlin type hierarchy (like Object in Java)
val obj: Any = "Hello"
val obj2: Any = 42
val obj3: Any = Order("ORD-001", 299.99, "CONFIRMED")

// Any has only 3 methods: equals, hashCode, toString
// Use Any? for nullable root type

(3) Unit: The Typed "No Return Value"

KOTLIN
// Unit is like void in Java, but it's a real type
fun logOrder(order: Order): Unit {  // Unit return is optional
    println("Processing ${order.id}")
}

fun logOrder2(order: Order) {  // Equivalent - Unit inferred
    println("Processing ${order.id}")
}

// Unit is a singleton - can be used as a value
val unitValue: Unit = Unit

(4) Nothing: Never Reaches

KOTLIN
// Nothing means "this code never returns"
fun fail(message: String): Nothing {
    throw IllegalStateException(message)
}

// Useful for Elvis operator with exceptions
val customer = order.customer ?: fail("Customer required")

// Infinite loop also returns Nothing
fun infiniteLoop(): Nothing {
    while (true) { /* never returns */ }
}

(5) Three Top-Level Types Comparison

Type Meaning Use Case Java Equivalent
Any Parent of all non-null types Generic references Object
Unit No meaningful return value Side-effect functions void
Nothing Never reaches the end Throw exceptions / infinite loops None

7. Type Aliases (typealias)

KOTLIN
// Simplify complex type signatures
typealias OrderMap = Map<String, List<Order>>
typealias OrderProcessor = (List<Order>) -> List<String>
typealias USD = Double

// Usage
val ordersByCustomer: OrderMap = mapOf(
    "Alice" to listOf(Order("ORD-001", 299.99, "CONFIRMED"))
)

typealias Predicate<T> = (T) -> Boolean
val isHighValue: Predicate<Order> = { it.total > 10_000 }

// typealias does NOT create new types - just aliases
val map: OrderMap = ordersByCustomer  // Same type

8. Complete Example: OrderProcessor Type-Safe Processing

KOTLIN
// ============================================
// OrderProcessor - Type-Safe Event Processing
// Feature: Process order events with smart cast
// ============================================

typealias OrderId = String
typealias USD = Double

sealed class OrderEvent {
    data class Created(val orderId: OrderId, val total: USD) : OrderEvent()
    data class Paid(val orderId: OrderId, val amount: USD) : OrderEvent()
    data class Shipped(val orderId: OrderId, val trackingCode: String) : OrderEvent()
    data class Cancelled(val orderId: OrderId, val reason: String) : OrderEvent()
}

fun handleEvent(event: OrderEvent): String = when (event) {
    is OrderEvent.Created -> {
        // Smart cast: event.orderId and event.total available
        val threshold = 10_000
        val priority = if (event.total > threshold) "VIP" else "STANDARD"
        "Order ${event.orderId} created (\$${event.total} USD) -> $priority"
    }
    is OrderEvent.Paid -> {
        val tax = event.amount * 0.08
        "Order ${event.orderId} paid: \$${event.amount} USD (tax: \$$tax USD)"
    }
    is OrderEvent.Shipped ->
        "Order ${event.orderId} shipped: ${event.trackingCode}"
    is OrderEvent.Cancelled ->
        "Order ${event.orderId} cancelled: ${event.reason}"
}

fun main() {
    val events: List<OrderEvent> = listOf(
        OrderEvent.Created("ORD-001", 299.99),
        OrderEvent.Created("ORD-002", 15_000.00),
        OrderEvent.Paid("ORD-001", 299.99),
        OrderEvent.Shipped("ORD-001", "TRK-ABC123"),
        OrderEvent.Cancelled("ORD-003", "Customer request")
    )

    events.forEach { event ->
        val result = handleEvent(event)
        println(result)
    }

    // Type check with smart cast
    val anyEvent: Any = events[0]
    if (anyEvent is OrderEvent.Created) {
        println("\nSmart cast works: ${anyEvent.orderId} costs \$${anyEvent.total} USD")
    }
}

Output:

TEXT
Order ORD-001 created ($299.99 USD) -> STANDARD
Order ORD-002 created ($15000.0 USD) -> VIP
Order ORD-001 paid: $299.99 USD (tax: $23.999200000000002 USD)
Order ORD-001 shipped: TRK-ABC123
Order ORD-003 cancelled: Customer request

Smart cast works: ORD-001 costs $299.99 USD

❓ FAQ

Q Are Kotlin's Int and Java's int the same thing?
A On the JVM, Kotlin's Int compiles to Java's int (primitive type), and auto-boxes to Integer when an object is needed. Developers don't need to worry about this difference.
Q When does smart cast not work?
A When a variable is var and may be modified concurrently, or has a custom getter, the compiler cannot guarantee consistent types between check and use — smart cast won't apply.
Q What is the practical use of the Nothing type?
A It's mainly used to mark functions that "never return" (e.g., throwing exceptions), helping the compiler understand that subsequent code is unreachable, which enables smart casts and Elvis operator usage.
Q Does typealias create a new type?
A No. typealias is just a type alias — the compiler replaces it with the original type. For true type-safe wrappers, use inline class / value class.
Q Why doesn't Kotlin support implicit type conversions?
A Because implicit narrowing conversions (e.g., Long→Int) silently lose data and are a common source of bugs. Explicit conversions make developers aware of potential data loss.
Q What's the difference between Any and Any??
A Any is the parent of all non-null types; Any? is the parent of all types (including nullable). Any? = Any | null is the true top of Kotlin's type system.

📖 Summary


📝 Exercises

  1. Beginner (⭐): Declare variables of different types (Int, Double, Boolean, String) and print their types ::class. Hint: use val x = 42; println(x::class)
  2. Intermediate (⭐⭐): Write a function that takes Any, using smart cast to handle String (print length) and Int (print squared value). Hint: is String / is Int
  3. Challenge (⭐⭐⭐): Use typealias and sealed classes to design an order event type system, with when exhaustively handling all event types. Hint: see Section 8's complete example

← Previous | Next →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏