際際滷

際際滷Share a Scribd company logo
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
}

More Related Content

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

  • 1. Kotlin vs. Java Some Java issues addressed in Kotlin
  • 2. 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
  • 3. Null references are controlled by the type system https://kotlinlang.org/docs/reference/null-safety.html
  • 4. 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
  • 5. 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") }
  • 6. 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
  • 7. 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
  • 8. 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
  • 9. 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
  • 10. 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()
  • 11. 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
  • 12. 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
  • 13. Methods returning void If a Java method returns void, it will return Unit when called from Kotlin
  • 14. 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
  • 15. 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!
  • 16. 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>!
  • 17. 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
  • 18. 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
  • 19. 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.
  • 20. 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.
  • 21. 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.
  • 22. 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)) { // ... }
  • 23. Arrays in Kotlin are 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, 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>
  • 27. 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]
  • 28. Kotlin has proper function 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 }
  • 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 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
  • 34. 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 // ... }
  • 35. 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! }
  • 36. 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>) { // ... }
  • 37. 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
  • 38. 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?>
  • 39. 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)
  • 40. Kotlin does not have checked exceptions https://kotlinlang.org/docs/reference/exceptions.html
  • 41. 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 }