Kotlin vs. Java Some Java issues addressed in Kotlin
Kotlin fixes a series of issues that Java suffers from — Null references are controlled by the type system. — No raw types — Arrays in Kotlin are invariant — Kotlin has proper function types, as opposed to Java's SAM-conversions — Use-site variance without wildcards — Kotlin does not have checked exceptions
Null references are controlled by the type system https://kotlinlang.org/docs/reference/null-safety.html
Nullable types and Non-Null Types var a: String = "abc" a = null // compilation error To allow nulls, we can declare a variable as nullable string, written String? var b: String? = "abc" b = null // ok val l = a.length val l = b.length // error: variable 'b' can be null
Checking for null in conditions val l = if (b != null) b.length else -1 if (b != null && b.length > 0) { print("String of length ${b.length}") } else { print("Empty string") }
Safe Calls b?.length This returns b.length if b is not null, and null otherwise. The type of this expression is Int? bob?.department?.head?.name Such a chain returns null if any of the properties in it is null
Elvis Operator When we have a nullable reference r, we can say "if r is not null, use it, otherwise use some non-null value x" val l: Int = if (b != null) b.length else -1 Along with the complete if-expression, this can be expressed with the Elvis operator, written ?: val l = b?.length ?: -1
The !! Operator We can write b!!, and this will return a non-null value of b or throw an NPE if b is null val l = b!!.length If you want an NPE, you can have it, but you have to ask for it explicitly, and it does not appear out of the blue
Safe Casts Regular casts may result into a ClassCastException if the object is not of the target type Another option is to use safe casts that return null if the attempt was not successful val aInt: Int? = a as? Int
Collections of Nullable Type If you have a collection of elements of a nullable type and want to filter non-null elements, you can do so by using filterNotNull val nullableList: List<Int?> = listOf(1, 2, null, 4) val intList: List<Int> = nullableList.filterNotNull()
No raw types Existing Java code can be called from Kotlin in a natural way, and Kotlin code can be used from Java rather smoothly as well
Getters and Setters Methods that follow the Java conventions for getters and setters are represented as properties in Kotlin Boolean accessor methods are represented as properties which have the same name as the getter method Note that, if the Java class only has a setter, it will not be visible as a property in Kotlin, because Kotlin does not support set-only properties at this time
Methods returning void If a Java method returns void, it will return Unit when called from Kotlin
Null-Safety and Platform Types val list = ArrayList<String>() // non-null (constructor result) list.add("Item") val size = list.size // non-null (primitive int) val item = list[0] // platform type inferred (ordinary Java object) item.substring(1) // allowed, may throw an exception if item == null val nullable: String? = item // allowed, always works val notNull: String = item // allowed, may fail at runtime
Mapped types Java type Kotlin type byte kotlin.Byte short kotlin.Short int kotlin.Int long kotlin.Long char kotlin.Char float kotlin.Float double kotlin.Double boolean kotlin.Boolean Java type Kotlin type java.lang.Object kotlin.Any! java.lang.Cloneable kotlin.Cloneable! java.lang.Comparable kotlin.Comparable! java.lang.Enum kotlin.Enum! java.lang.Annotation kotlin.Annotation! java.lang.Deprecated kotlin.Deprecated! java.lang.CharSequence kotlin.CharSequence! java.lang.String kotlin.String! java.lang.Number kotlin.Number! java.lang.Throwable kotlin.Throwable!
Mapped types Java type Kotlin type java.lang.Byte kotlin.Byte? java.lang.Short kotlin.Short? java.lang.Integer kotlin.Int? java.lang.Long kotlin.Long? java.lang.Character kotlin.Char? java.lang.Float kotlin.Float? java.lang.Double kotlin.Double? java.lang.Boolean kotlin.Boolean? Java type Kotlin type int[] kotlin.IntArray! String[] kotlin.Array<(out) String>!
Java type Kotlin read-only type Kotlin mutable type Loaded platform type Iterator<T> Iterator<T> MutableIterator<T> (Mutable)Iterator<T>! Iterable<T> Iterable<T> MutableIterable<T> (Mutable)Iterable<T>! Collection<T> Collection<T> MutableCollection<T> (Mutable)Collection<T>! Set<T> Set<T> MutableSet<T> (Mutable)Set<T>! List<T> List<T> MutableList<T> (Mutable)List<T>! ListIterator<T> ListIterator<T> MutableListIterator<T> (Mutable)ListIterator<T>! Map<K, V> Map<K, V> MutableMap<K, V> (Mutable)Map<K, V>! Map.Entry<K, V> Map.Entry<K, V> MutableMap.MutableEnt ry<K,V> (Mutable)Map.(Mutable)En try<K, V>! Mapped types
Java generics in Kotlin Foo<? extends Bar> Foo<? super Bar> List Foo<out Bar!>! Foo<in Bar!>! List<*>!, i.e. List<out Any?>!. if (a is List<Int>) // Error: cannot check if it is really a List of Ints // but if (a is List<*>) // OK: no guarantees about the contents of the list
Java Arrays Arrays in Kotlin are invariant, unlike Java. There are specialized classes for every type of primitive array (IntArray, DoubleArray, CharArray, and so on) for maximum performance.
Java Varargs public class JavaArrayExample { public void removeIndicesVarArg(int... indices) { // code here... } } In that case you need to use the spread operator * to pass the IntArray: val javaObj = JavaArrayExample() val array = intArrayOf(0, 1, 2, 3) javaObj.removeIndicesVarArg(*array) It's currently not possible to pass null to a method that is declared as varargs.
Inheritance from Java classes At most one Java class (and as many Java interfaces as you like) can be a supertype for a class in Kotlin.
Accessing static members Static members of Java classes form "companion objects" for these classes. We cannot pass such a "companion object" around as a value, but can access the members explicitly, for example: if (Character.isLetter(a)) { // ... }
Arrays in Kotlin are invariant https://kotlinlang.org/docs/reference/basic-types.html#arrays
Class Array class Array<T> private constructor() { val size: Int operator fun get(index: Int): T operator fun set(index: Int, value: T): Unit operator fun iterator(): Iterator<T> // ... }
Create an Array arrayOf(1, 2, 3) creates an array [1, 2, 3] Alternatively, the arrayOfNulls() library function can be used to create an array of a given size filled with null elements // Creates an Array<String> with values ["0", "1", "4", "9", "16"] val asc = Array(5, { i -> (i * i).toString() })
Unlike Java, arrays in Kotlin are invariant This means that Kotlin does not let us assign an Array<String> to an Array<Any>, which prevents a possible runtime failure But you can use Array<out Any>
Classes to represent arrays of primitive types — ByteArray — ShortArray — IntArray — and so on val x: IntArray = intArrayOf(1, 2, 3) x[0] = x[1] + x[2]
Kotlin has proper function types, as opposed to Java's SAM-conversions
Higher-Order Functions fun <T> lock(lock: Lock, body: () -> T): T { lock.lock() try { return body() } finally { lock.unlock() } }
Function Types fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? { var max: T? = null for (it in collection) if (max == null || less(max, it)) max = it return max }
Use-site variance without wildcards
Generics class Box<T>(t: T) { var value = t } To create an instance of such a class, we need to provide the type arguments: val box: Box<Int> = Box<Int>(1)
Variance One of the most tricky parts of Java's type system is wildcard types And Kotlin doesn't have any Instead, it has two other things: ● declaration-site variance ● type projections
Declaration-site variance (variance annotation out) interface Source<T> { T nextT(); } void demo(Source<String> strs) { Source<Object> objects = strs; // !!! Not allowed in Java // ... } abstract class Source<out T> { abstract fun nextT(): T } fun demo(strs: Source<String>) { val objects: Source<Any> = strs // This is OK, since T is an out-parameter // ... }
Declaration-site variance (variance annotation in) abstract class Comparable<in T> { abstract fun compareTo(other: T): Int } fun demo(x: Comparable<Number>) { x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number // Thus, we can assign x to a variable of type Comparable<Double> val y: Comparable<Double> = x // OK! }
Use-site variance: Type projections class Array<T>(val size: Int) { fun get(index: Int): T { /* ... */ } fun set(index: Int, value: T) { /* ... */ } } fun copy(from: Array<Any>, to: Array<Any>) { assert(from.size == to.size) for (i in from.indices) to[i] = from[i] } val ints: Array<Int> = arrayOf(1, 2, 3) val any = Array<Any>(3) { "" } copy(ints, any) // Error: expects (Array<Any>, Array<Any>). fun copy(from: Array<out Any>, to: Array<Any>) { // ... }
Star-projection syntax — For Foo<out T>, where T is a covariant type parameter with the upper bound TUpper, Foo<*> is equivalent to Foo<out TUpper>. It means that when the T is unknown you can safely read values of TUpper from Foo<*> — For Foo<in T>, where T is a contravariant type parameter, Foo<*> is equivalent to Foo<in Nothing>. It means there is nothing you can write to Foo<*> in a safe way when T is unknown — For Foo<T>, where T is an invariant type parameter with the upper bound TUpper, Foo<*> is equivalent to Foo<out TUpper> for reading values and to Foo<in Nothing> for writing values
If a generic type has several type parameters each of them can be projected independently. For example, if the type is declared as interface Function<in T, out U> we can imagine the following star-projections: — Function<*, String> means Function<in Nothing, String> — Function<Int, *> means Function<Int, out Any?> — Function<*, *> means Function<in Nothing, out Any?>
Generic functions Not only classes can have type parameters. Functions can, too: fun <T> singletonList(item: T): List<T> { // ... } fun <T> T.basicToString() : String { // extension function // ... } To call a generic function, specify the type arguments at the call site after the name of the function: val l = singletonList<Int>(1)
Kotlin does not have checked exceptions https://kotlinlang.org/docs/reference/exceptions.html
Checked Exceptions The following is an example interface of the JDK implemented by StringBuilder class: Appendable append(CharSequence csq) throws IOException; try { log.append(message) } catch (IOException e) { // Must be safe }

1 kotlin vs. java: some java issues addressed in kotlin

  • 1.
    Kotlin vs. Java SomeJava issues addressed in Kotlin
  • 2.
    Kotlin fixes aseries of issues that Java suffers from — Null references are controlled by the type system. — No raw types — Arrays in Kotlin are invariant — Kotlin has proper function types, as opposed to Java's SAM-conversions — Use-site variance without wildcards — Kotlin does not have checked exceptions
  • 3.
    Null references arecontrolled by the type system https://kotlinlang.org/docs/reference/null-safety.html
  • 4.
    Nullable types andNon-Null Types var a: String = "abc" a = null // compilation error To allow nulls, we can declare a variable as nullable string, written String? var b: String? = "abc" b = null // ok val l = a.length val l = b.length // error: variable 'b' can be null
  • 5.
    Checking for nullin conditions val l = if (b != null) b.length else -1 if (b != null && b.length > 0) { print("String of length ${b.length}") } else { print("Empty string") }
  • 6.
    Safe Calls b?.length This returnsb.length if b is not null, and null otherwise. The type of this expression is Int? bob?.department?.head?.name Such a chain returns null if any of the properties in it is null
  • 7.
    Elvis Operator When wehave a nullable reference r, we can say "if r is not null, use it, otherwise use some non-null value x" val l: Int = if (b != null) b.length else -1 Along with the complete if-expression, this can be expressed with the Elvis operator, written ?: val l = b?.length ?: -1
  • 8.
    The !! Operator Wecan write b!!, and this will return a non-null value of b or throw an NPE if b is null val l = b!!.length If you want an NPE, you can have it, but you have to ask for it explicitly, and it does not appear out of the blue
  • 9.
    Safe Casts Regular castsmay result into a ClassCastException if the object is not of the target type Another option is to use safe casts that return null if the attempt was not successful val aInt: Int? = a as? Int
  • 10.
    Collections of NullableType If you have a collection of elements of a nullable type and want to filter non-null elements, you can do so by using filterNotNull val nullableList: List<Int?> = listOf(1, 2, null, 4) val intList: List<Int> = nullableList.filterNotNull()
  • 11.
    No raw types ExistingJava code can be called from Kotlin in a natural way, and Kotlin code can be used from Java rather smoothly as well
  • 12.
    Getters and Setters Methodsthat follow the Java conventions for getters and setters are represented as properties in Kotlin Boolean accessor methods are represented as properties which have the same name as the getter method Note that, if the Java class only has a setter, it will not be visible as a property in Kotlin, because Kotlin does not support set-only properties at this time
  • 13.
    Methods returning void Ifa Java method returns void, it will return Unit when called from Kotlin
  • 14.
    Null-Safety and PlatformTypes val list = ArrayList<String>() // non-null (constructor result) list.add("Item") val size = list.size // non-null (primitive int) val item = list[0] // platform type inferred (ordinary Java object) item.substring(1) // allowed, may throw an exception if item == null val nullable: String? = item // allowed, always works val notNull: String = item // allowed, may fail at runtime
  • 15.
    Mapped types Java typeKotlin type byte kotlin.Byte short kotlin.Short int kotlin.Int long kotlin.Long char kotlin.Char float kotlin.Float double kotlin.Double boolean kotlin.Boolean Java type Kotlin type java.lang.Object kotlin.Any! java.lang.Cloneable kotlin.Cloneable! java.lang.Comparable kotlin.Comparable! java.lang.Enum kotlin.Enum! java.lang.Annotation kotlin.Annotation! java.lang.Deprecated kotlin.Deprecated! java.lang.CharSequence kotlin.CharSequence! java.lang.String kotlin.String! java.lang.Number kotlin.Number! java.lang.Throwable kotlin.Throwable!
  • 16.
    Mapped types Java typeKotlin type java.lang.Byte kotlin.Byte? java.lang.Short kotlin.Short? java.lang.Integer kotlin.Int? java.lang.Long kotlin.Long? java.lang.Character kotlin.Char? java.lang.Float kotlin.Float? java.lang.Double kotlin.Double? java.lang.Boolean kotlin.Boolean? Java type Kotlin type int[] kotlin.IntArray! String[] kotlin.Array<(out) String>!
  • 17.
    Java type Kotlinread-only type Kotlin mutable type Loaded platform type Iterator<T> Iterator<T> MutableIterator<T> (Mutable)Iterator<T>! Iterable<T> Iterable<T> MutableIterable<T> (Mutable)Iterable<T>! Collection<T> Collection<T> MutableCollection<T> (Mutable)Collection<T>! Set<T> Set<T> MutableSet<T> (Mutable)Set<T>! List<T> List<T> MutableList<T> (Mutable)List<T>! ListIterator<T> ListIterator<T> MutableListIterator<T> (Mutable)ListIterator<T>! Map<K, V> Map<K, V> MutableMap<K, V> (Mutable)Map<K, V>! Map.Entry<K, V> Map.Entry<K, V> MutableMap.MutableEnt ry<K,V> (Mutable)Map.(Mutable)En try<K, V>! Mapped types
  • 18.
    Java generics inKotlin Foo<? extends Bar> Foo<? super Bar> List Foo<out Bar!>! Foo<in Bar!>! List<*>!, i.e. List<out Any?>!. if (a is List<Int>) // Error: cannot check if it is really a List of Ints // but if (a is List<*>) // OK: no guarantees about the contents of the list
  • 19.
    Java Arrays Arrays inKotlin are invariant, unlike Java. There are specialized classes for every type of primitive array (IntArray, DoubleArray, CharArray, and so on) for maximum performance.
  • 20.
    Java Varargs public classJavaArrayExample { public void removeIndicesVarArg(int... indices) { // code here... } } In that case you need to use the spread operator * to pass the IntArray: val javaObj = JavaArrayExample() val array = intArrayOf(0, 1, 2, 3) javaObj.removeIndicesVarArg(*array) It's currently not possible to pass null to a method that is declared as varargs.
  • 21.
    Inheritance from Javaclasses At most one Java class (and as many Java interfaces as you like) can be a supertype for a class in Kotlin.
  • 22.
    Accessing static members Staticmembers of Java classes form "companion objects" for these classes. We cannot pass such a "companion object" around as a value, but can access the members explicitly, for example: if (Character.isLetter(a)) { // ... }
  • 23.
    Arrays in Kotlinare invariant https://kotlinlang.org/docs/reference/basic-types.html#arrays
  • 24.
    Class Array class Array<T>private constructor() { val size: Int operator fun get(index: Int): T operator fun set(index: Int, value: T): Unit operator fun iterator(): Iterator<T> // ... }
  • 25.
    Create an Array arrayOf(1,2, 3) creates an array [1, 2, 3] Alternatively, the arrayOfNulls() library function can be used to create an array of a given size filled with null elements // Creates an Array<String> with values ["0", "1", "4", "9", "16"] val asc = Array(5, { i -> (i * i).toString() })
  • 26.
    Unlike Java, arraysin Kotlin are invariant This means that Kotlin does not let us assign an Array<String> to an Array<Any>, which prevents a possible runtime failure But you can use Array<out Any>
  • 27.
    Classes to representarrays of primitive types — ByteArray — ShortArray — IntArray — and so on val x: IntArray = intArrayOf(1, 2, 3) x[0] = x[1] + x[2]
  • 28.
    Kotlin has properfunction types, as opposed to Java's SAM-conversions
  • 29.
    Higher-Order Functions fun <T>lock(lock: Lock, body: () -> T): T { lock.lock() try { return body() } finally { lock.unlock() } }
  • 30.
    Function Types fun <T>max(collection: Collection<T>, less: (T, T) -> Boolean): T? { var max: T? = null for (it in collection) if (max == null || less(max, it)) max = it return max }
  • 31.
  • 32.
    Generics class Box<T>(t: T){ var value = t } To create an instance of such a class, we need to provide the type arguments: val box: Box<Int> = Box<Int>(1)
  • 33.
    Variance One of themost tricky parts of Java's type system is wildcard types And Kotlin doesn't have any Instead, it has two other things: ● declaration-site variance ● type projections
  • 34.
    Declaration-site variance (varianceannotation out) interface Source<T> { T nextT(); } void demo(Source<String> strs) { Source<Object> objects = strs; // !!! Not allowed in Java // ... } abstract class Source<out T> { abstract fun nextT(): T } fun demo(strs: Source<String>) { val objects: Source<Any> = strs // This is OK, since T is an out-parameter // ... }
  • 35.
    Declaration-site variance (varianceannotation in) abstract class Comparable<in T> { abstract fun compareTo(other: T): Int } fun demo(x: Comparable<Number>) { x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number // Thus, we can assign x to a variable of type Comparable<Double> val y: Comparable<Double> = x // OK! }
  • 36.
    Use-site variance: Typeprojections class Array<T>(val size: Int) { fun get(index: Int): T { /* ... */ } fun set(index: Int, value: T) { /* ... */ } } fun copy(from: Array<Any>, to: Array<Any>) { assert(from.size == to.size) for (i in from.indices) to[i] = from[i] } val ints: Array<Int> = arrayOf(1, 2, 3) val any = Array<Any>(3) { "" } copy(ints, any) // Error: expects (Array<Any>, Array<Any>). fun copy(from: Array<out Any>, to: Array<Any>) { // ... }
  • 37.
    Star-projection syntax — ForFoo<out T>, where T is a covariant type parameter with the upper bound TUpper, Foo<*> is equivalent to Foo<out TUpper>. It means that when the T is unknown you can safely read values of TUpper from Foo<*> — For Foo<in T>, where T is a contravariant type parameter, Foo<*> is equivalent to Foo<in Nothing>. It means there is nothing you can write to Foo<*> in a safe way when T is unknown — For Foo<T>, where T is an invariant type parameter with the upper bound TUpper, Foo<*> is equivalent to Foo<out TUpper> for reading values and to Foo<in Nothing> for writing values
  • 38.
    If a generictype has several type parameters each of them can be projected independently. For example, if the type is declared as interface Function<in T, out U> we can imagine the following star-projections: — Function<*, String> means Function<in Nothing, String> — Function<Int, *> means Function<Int, out Any?> — Function<*, *> means Function<in Nothing, out Any?>
  • 39.
    Generic functions Not onlyclasses can have type parameters. Functions can, too: fun <T> singletonList(item: T): List<T> { // ... } fun <T> T.basicToString() : String { // extension function // ... } To call a generic function, specify the type arguments at the call site after the name of the function: val l = singletonList<Int>(1)
  • 40.
    Kotlin does nothave checked exceptions https://kotlinlang.org/docs/reference/exceptions.html
  • 41.
    Checked Exceptions The followingis an example interface of the JDK implemented by StringBuilder class: Appendable append(CharSequence csq) throws IOException; try { log.append(message) } catch (IOException e) { // Must be safe }