Skip to main content

Notes - Kotlin Types: Exposed by Svetlana Isakova (KotlinConf 2017)

This article was translated from JavaScript by Claude Code.

※ In Notes articles, I write down (roughly) in English the content of English session videos or podcasts. This article is intended to be read as a reference while watching the video. I hope this article is helpful when watching the video. (If there are any errors or typos in the content, please let me know.)

This article is about Kotlin Types: Exposed by Svetlana Isakova (KotlinConf 2017).

Agenda
#

  • Basic Types

  • Nullable types

  • Collection types

How those types interact with Java and what happens when we mix types of Kotlin and Java

fun foo(): Int = 1

fun bar(): Int? = 1
public static final int foo() {
  return 1;
}

public static final Integer bar() {
  return Integer.of(1);
}

If you want to know what is going on under the hood, you can “show kotlin bytecode” (and also decompile the bytecode) in Android Studio

Correspondence between Kotlin and Java types

  • Int in Kotlin => int in Java

  • Int? in Kotlin => java.lang.Integer in Java

Kotlin does not have primitive types in the language, yet Kotlin still has primitive types in the bytecode level by making use of wrapper types and nullable types

Generic arguments
#

  • List<Int> in Kotlin => List<Integer> in Java

  • Array<Int> in Kotlin => Integer[] in Java

Arrays of primitive types
#

If you want to use primitive types of array, then use IntArray in Kotlin:

  • IntArray in Kotlin => int[] in Java

String
#

kotlin.String in Kotlin => java.lang.String in Java

kotlin.String hides some confusing methods:

e.g. replaceAll method

In Java:

"one.two.".replaceAll(".", "*") => *******

In Kotlin:

"one.two.".replace(".", "*") => one*two
"one.two.".replace(".".toRegex(), "*") => one*two

Any
#

  • Any in Kotlin => java.lang.Object in Java

  • Any type is the super type for ALL types, including Int or anything

Boxing under the hood
#
log(2017) // the value is autoboxed

fun log(any: Any) {
  println("Value: $any")
}

fun log(i: Int) {
  println("Value: $i")
}

Unit (Kotlin) vs Nothing (Kotlin) vs void (Java)?
#

The concept comes from functional programming; from the type system

  • Unit: “a type that allows only one value and thus can hold no information” => The function completes

  • Nothing: “a type that has no values” => The function never completes

Unit instead of void
#

Whenever you use void in Java, you use Unit in Kotlin

Unit

  • No meaningful value is returned

  • No explicit returned type implies you return Unit by default

  • Two equivalent syntactic forms:

fun f() { /*…*/ }

fun f(): Unit { /*…*/ }

Under the hood, Unit is still void in the bytecode

Nothing is different to Unit/void
#

Unit and Nothing are two very different types even though in the bytecode level both mean void type

  • Nothing means “this function never returns”
Any & Nothing types
#
  • Any is a super type for all the other types

  • Nothing is a subtype for all the other types

e.g. fail function below returns Unit

val answer = if (timeHasPassed()) {
  42
} else {
  fail("Not ready")
}

fun fail(message: String) {
  throw IllegalStateException(message)
}
  • If 42 is returned then the returned type of the expression function is Int

  • If fail function is called then the returned type of the expression function is Unit

=> Kotlin compiler thinks that the expression function returns Any type because the super type of both Int and Unit is Any

Returning Any means the variable answer will be initialized even if it fails, which is not what we expect; we expect it not to be initialized when it fails. => We can use Nothing in this case

Nothing is a subtype for all the other types, so that means the returned type of the expression function is now Int, which is what we expect

Type of null
#

var user = null // => var user: Nothing? = null
user = User("svtk") // Error: Type mismatch

val users = mutableListOf(null) // => var users: List<Nothing?> = mutableListOf(null)
users.add(User("svtk")) // Error: Type mismatch

Nullable types & Java
#

Nullable Types Under the Hood => Just annotations

If your returned type is annotated with @Nullable in Java, then in Kotlin, the type will become nullable

How to still prevent NPEs?
#

  • Annotate your Java types

    • @Nullable type

    • @NotNull type

Non-null by default (JSR-305):

@javax.annotation.Nonnull
@TypeQualifierDefault(ElementType.PARAMETER, )
annotation class MyNonnullByDefault

package-info.java:

@MyNonnullByDefault
package mypackage;

Then, Kotlin will give you warning when assigning null to values:

@MyNonnullByDefault
public class Session {
  public void setDescription(String description) {
    this.description = description;
  }
}

Calling Java code from Kotlin:

val session = Session()
session.setDescription(null) // => Warning: Expected type doesn't accept nulls in Java …

If you prefer errors in compile time rather than warnings:

build.gradle

compileKotlin {
  kotlinOptions {
    freeCompilerArgs += "-Xjsr305=strict"
  }
}

Then, Kotlin will give you errors when assigning null to values:

@MyNonnullByDefault
public class Session {
  public void setDescription(String description) {
    this.description = description;
  }
}

Calling Java code from Kotlin:

val session = Session()
session.setDescription(null) // => Error: Null can not be a value of a non-null type …
  • Specify types explicitly
public class Session {
  public void setDescription(String description) {
    this.description = description;
  }
}
val session = Session()
val description: String? = session.description 

Or, the code below gives you IllegalStateException in runtime which notifies you very early what is wrong with your code:

val session = Session()
val description: String = session.description // IllegalStateException: session.description must not be null

How it works?

  • Intrinsic checks

The code below will be generated by compiler:

Intrinsics.checkExpressionValueisNotNull()

Collections
#

Read-only interfaces improve API

  • How read-only collections and mutable collections work in Kotlin

List & MutableList
#

  • Two interfaces declared in kotlin package

  • MutableList extends List

Read-only DOES NOT mean immutable

  • Read-only interface just lacks mutating methods

The actual list can be changed by another reference:

val mutableList = mutableListOf(1, 2, 3)
val list: List<Int> = mutableList

println(list) // [1, 2, 3]

mutableList.add(4)
println(list) // [1, 2, 3, 4]

In Kotlin, there is a plan to provide immutable data structures in the standard library, but it is not ready yet

  • String! = notation, not syntax; platform type, which is a type that comes from Java

Platform types: summary

Good compromise between safety and convenience

Summary
#

  • Primitives under the hood, and boxing is possible

  • Use Nothing whenever possible

  • Preventing NPE for Java interop: annotations, explicit types

  • Read-only does not mean immutable

That’s all!