Kotlin addresses several issues in Java by controlling null references in the type system, eliminating raw types, making arrays invariant, defining proper function types, allowing use-site variance without wildcards, and removing checked exceptions. It fixes null references by allowing nullable types and non-null types, provides null-safe calls and operators like Elvis and safe casts, and filters nulls from collections.
1 of 41
Download to read offline
More Related Content
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
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>!
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
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
}