6. History of Kotlin?
2010: JetBrains started the Kotlin Project
2011: Public Announcement
2012: Open Sourced under Apache 2
2016: Release 1.0
2017: Release 1.1 Google announced first class language support
2018: Release 1.2
Release 2.0?? No indication but probably 2019+
._X = bug fixes e.g. 1.02
_.X = incremental, no major changes e.g. 1.1, 1.2
X.0 = major (may introduce major features, deprecate others) e.g. 2.0
11. What differs from Java
Non-nullable Type System
All types are non-nullable by
default (but can and may have to
be nullable in some
circumstances)
No Covariant Arrays
Covariant arrays are gone, now
invariant.
No Primitives
Everything in Kotlin is an
Object.
No Checked Exceptions
Don’t exist in the class
hierarchy of Errors.
No Statics
No statics exist in the language,
at least conceptually, but likely
to change
No Raw types
No need for backward JVM
compatibility, we also have
reification!
AND Top level properties and funs, Data Classes, Co-routines, Smartcasting, Operator Overloading,
Properties, Extension Functions, Companion Objects, Mutable/Immutable collections, Singletons, String
templates, Range expressions, Delegation, Generics … NO SEMICOLONS!!!
12. Some of what it doesn’t have…
Multi-Catch
For Exception handling
True immutability
Same implementation as Java
reference types
Value Types
Beyond simple JVM types
JVM limitation?
PP Visibility/Namespaces
For use in Java interoperability.
Array Slices
data[a:b], data[:b] etc.
SAM Conversion
Only for Java not for Kotlin
interfaces
No Boolean Op Overloading
All other operators are however
overloadable
14. Class Hierarchy - Any and Nothing
Any
Equivalent to java.lang.Object*
Nothing
Is not Void! (Unit type is Void)
Note:Technically Object is actually
a subtype of Any (TBD)
Diagram courtesy of JetBrains
15. Interface and Implementation Inheritance
private interface IfMyInterface : IfSomeOtherInterface{
val x get() = 100
fun someMethoda(x: Any?) : Unit /* contract unfulfilled */
fun someMethodb(x: Int) : Int = 123 /* Java 8’ish */
}
open class SomeClass /* open for extension – final by default! */
abstract class SomeAbstractClass : Any()
final class MyClass : SomeClass(), IfMyInterface {
override fun someMethoda(x: Any?) {
/* body */
}
}
class SubClass : SomeAbstractClass() {
/* implement the abstract class method bodies */
}
Note:
Syntax is very
close to Java and
readable by any
Java developer.
16. Class Basics
//constructor keyword optional
//default parameters
public class TopLevelClass private constructor(val name:String="unknown", val age:Int =-1) {
var t = 10
//secondary C
constructor(name:String, age:Int, gender:String ="unknown") : this(name, age) {}
//tripe, just to show local instantiation
inner class InnerClass(){
val innert = t
val tlc = TopLevelClass("jane")
}
class AClass{
val nestedt = t
fun showme() = 123
}
}
/* … */
/* call with a named parameter, and using default parameters */
val tlc = TopLevelClass("john", 22, gender="male") //call 3-arg C
17. Sealed Class
abstract sealed class Sealy { /* default private C */
class Red : Sealy()
class Green : Sealy()
}
/* … */
val sealy : Sealy = Sealy.Red()
val output = when (sealy) {
is Sealy.Red -> "red"
is Sealy.Green -> "green"
/* no else branch! */
}
println("result is ${output}") //shortened println syntax and String templates
Primary benefit: Exhaustive case when expression. Specified set of specific values (such as a state
machine). Similar to an enum class, effectively abstract so not instantiable.
18. Data Class
data class Student(val name: String, var age: Int)
This is the entire class (no body)!
getters and if applicable setters autogenerated
hashcode/equals pair for the object (structural equivalence)
Custom toString (in the form of (name=John, age=30) )
Restriction: Can’t be opened for extension, abstract, sealed or inner.
Copy extension function (with optional modification)
val stu = Student("ed", 42)
val stu_amended_copy = stu.copy(age=43) //not referential!
20. Anonymous Class
var clickCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
clickCount++ //true closure!
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
Unlike older versions of Java…
21. Companion Objects
Quite possible at 2.0 will be deprecated in favour of static objects
Possibly better to write top level functions and variables before the class rather than use a
Companion Object- elizarov JetBrains Team Aug ’17 (most of the time.. No @JvmStatic)
public class Penka {
val id: Int? = null
init {
id = nextId?.inc()
}
private companion object : SomeClass
var nextId : Int? = 1
fun masha() {}
}
}
fun main(args: Array<String>) {
Penka()
println(Penka().id!!)
}
//can inherit!
public class Lenka {
Integer id;
{
id = nextId++;
}
private static Integer nextId = 1;
static void masha{}
public static void main(String[] args) {
new Lenka();
System.out.println(new Lenka().id);
}
}
var nextId : Int? = 1
fun masha() {}
//above package namespace
public class Penka {
val id: Int? = null
init {
id = nextId?.inc()
}
private companion object {
var nextId : Int? = 1
fun masha() {}
}
}
fun main(args: Array<String>) {
Penka()
println(Penka().id!!)
}
22. Companion Objects
Simply put a companion object is simply a final static method!
File: MyTest.kt
package foo
class Penka {
private companion object {
fun masha() {
/* some code */
}
}
}
public final class MyTestKt {
public static final void masha() {
/* our code */
}
}
23. Top Level Properties and Functions
We can have properties and methods at Top level
These are compiled down to JVM static methods within YourPackageName.class (delegates implementations to the so-named package-
parts)
Diagram courtesy of JetBrains
25. Smart Casts
fun demo(x: Any) {
if (x is String) {
print(x.length) // x is automatically cast to String
}
}
if (x !is String) return
print(x.length) // x is automatically cast to String
Safe “as?” /Unsafe “as” Cast
val x: String? = y as? String //safe cast.. return null if cast not possible
val x: String = y as String //unsafe cast, may throw CCE!
26. Extension Functions (add a method to any class)
Extension fun:
fun Int.appendString(s:String) = this.toString() + s 123.appendString("abc") 123abc
infix fun Int.appendString(s:String) = this.toString() + s 123 appendString "abc" 123abc
EBNF: [Infix] fun MyClassTobeExtended.myNoneOverridenMethod([arg]) [: returnType ] = expression | [{ /* block */ }]
In the result refer to the first argument as the receiver with this, and the arg as the parameter sent with the message
Likely a better way to do this by overloading :
operator fun Int.plus(s:String) = this.toString() + s 123 + "abc" 123abc
Java.lang.Integer
For obvious reasons doesn’t have an appendString method*
*I stand slightly corrected, "+“ is overloaded as to: String s = 123 + "abc“ which makes this example pretty useless
Note: We can’t override existing methods
27. Standard Functions on Any (let, apply, run)
public inline fun <T, R> T.run(block: T.() -> R): R = block()
myClassInstance.run { //we could also use a property and apply a method such as length
this.myVar = 123
this.funkyFunc(123)
} //returns the result of the block (last arg) and mutates the receiver
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
val person = myPersonInstance.apply {
this.name = "lenka penka"
this.age = 33
} //Calls the specified function [block] with "this" value as its receiver and returns "this" value.
//mutates receiver
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
"something here".let {println("The length of this String is ${it.length}")
} //Calls the specified function [block] with "this" value as its argument and returns its result.
28. Standard Functions on Any (let, apply, run)
if (listz.run {
this.add(11)
this.add(12)
} //mutates receiver and returns the last result (true)
){
listz.apply {
this.remove(1)
} //mutate and return receiver
.let { println(it) } //pass and print the receiver (implicit it)
}
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
val listz = arrayListOf<Int>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
31. Visibility Modifiers
Classes, objects, interfaces, constructors, functions, properties and their setters can have visibility modifiers
Top Level
Functions, properties and classes, objects and interfaces
public by default
private “file level” visibility
internal same module (i.e. PP)
Class Level
Members declared within a Class
public by default
private for scope inside class (and its members*)
internal same module (i.e. PP)
protected class + subclasses
*Outer cannot see private members of its inner classes,
nor protected, although you can extend an open inner class
within the original class to gain access to the protected
members!
32. Access Modifiers
Classes are…
final by default
open can be extended
package foo
open class MyClass
class bar : MyClass()
final class MyClass
class foo : MyClass()
By default classes cannot be extended (are final), this includes inner classes
33. Visibility/Access Modifiers Example
// file name: Example.kt
package foo
/* top level! */
private fun foo() {} // visible inside example.kt
public var bar: Int = 5 // property is visible everywhere
private set // setter is visible only in example.kt
internal val baz = 6 // visible inside foo
open class MyClass(){ //not default
private final inner class Inner(){} //reference to outer class
public final class Nested(){} //no refs to outer class
public final object Objy{} //effectively static/singleton
}
38. Kotlin Example Java Mapping
class Student(val name: String, uni:String)
fun main(args: Array<String>) {
val a = 123
val b = 444f
val c = "ABC"
val d = ("[A..Z]").toRegex()
val e = 1..100
val f = Student("Masha", “MGU")
val g = mutableListOf<Student>()
val h = hashMapOf<Int,Student>()
val i = arrayOf(a,b,c,d,e,f,g,h)
i.forEach { println(it.javaClass.name) }
}
Types are mostly inferred by the Type system, and need not be specified, most underlying types are
from the JDK, the few from Kotlin are additional types
40. Nullable Type System
Biggest Kotlin difference: A revised Type System that enforces non-nullability by default
var x = 100f /* non-nullable */
var y : Int? = 100 /* this is nullable */
var g : MutableList<Student>? = mutableListOf<Student>()
We simply add “?” After a type definition to indicate the type is
nullable, we must explicitly state (most) nullable types.
IntelliJ picks up static semantic abuse issues and forces use of various
operators to avoid R/T NPE.
41. Comparing Types
Referential Equality
=== (negated !==)
Points to same object
i.e. shallow
Value/Structural Equality
== (negated !=)
Points to same data
i.e. deep
a?.equals(b) ?: (b === null)
If a not null call equals(b:Any?) and return result
otherwise if a is null (using elvis) return b===null
(referential) i.e. null == null (true). Or null = not null
(false)
The above are used to provide safety working with nullable types in a type system with mixed typing
No "==" or ". equals()" confusion
Java == Java .equals
42. Some Type System Operators
Safe Call Operator ?.
Allows a check for
receiver nullability before
allowing access to a
method else return null if
the receiver is null
Elvis Operator ?:
Return a non-null
expression on the left
else return a value on
the right
Double Bang Operator !!
Converts any value to a non-null
type and throws an NPE if null
Example:
var foo : String? = "bar"
/* … */
val len = foo?.length()
Example:
var foo: String? = "bar"
/* … */
val len = (foo?.length) ?: -1
Example
var foo: String? = "bar"
/* … */
val len = foo!!.length
The above are used to provide safety working with nullable types in a type system with mixed typing
Safe Cast as?
Cast returns null and
not CCE if the cast fails
Example:
val aInt: Int? = a as? Int
44. Types Woes #2
Thankyou Ilya Ryzhenkov from JetBrains Kotlin Libraries Team for the overridden operator code.
operator fun Int?.inc() : Int? = this?.inc()
fun test() {
var i: Int? = null
i++
}
i++ (for nullables) i = if (i == null) null else i.inc()
Nullable
46. Some function Signatures
Java Kotlin
String jrasl() { return “abc” } fun justReturnAStringLiteral : String = “abc”
<T> void f(T a, Object b) fun <T> f(a:T, b:Any?) : Unit
<E> void a(Collection<? extends E> items) fun <E> a(items: Collection<out E>) : Unit
void checkConnection() throws IOException No equivalent
No equivalent fun willNeverReturn(): Nothing {
throw IOException()
}
47. Imperative Structures
Java Kotlin (can be expression)
for(int i=0;i<=10;i++) for(i in 0..10) //i implicit decl/local scope
while (a>0) {/*..*/} while (a>0) {/*..*/}
do { block } while (condition) do { block } while (condition)
String res; val res = if (x > 10) "foo" else "bar"
if (x>10) res="foo";
else res="bar";
if ((x>0) && (x<100)) if (x in 1..100)
for (MyType s : collection) for (item in collection)
switch - case when {...} / when(..) {...} etc
49. (Nullable) Platform Types
Java composite types have potential nullable references!
That is all reference types in the language (anything other than the basic value types)
When calling to Java we need to prepare for this!
Because we most often are passing back something that could cause us some issues
These types are called “Platform Types” in Kotlin
They are a special type and can cause us NPE when dereferenced because they are nullable
We can use Java-side annotations to pass information
Such as whether to treat the passed reference as @nullable etc.
52. From Kotlin to Java
Kotlin
//@JvmOverloads allows overloaded Constructors/funs from Java
class TopLevelClazz @JvmOverloads constructor( val name:String="unknown",
val age:Int =-1,
val gender:String="unknown") {}
Java
TopLevelClazz tlc1 = new TopLevelClazz("ed");
System.out.println(tlc1.getAge()); //auto-generated getter from Kotlin
TopLevelClazz tlc2 = new TopLevelClazz("ed",43);
TopLevelClazz tlc3 = new TopLevelClazz("ed",43,"male");
54. Kotlin Exceptions
//Kotlin side
@Throws(IOException::class)
fun foo() : Nothing {
throw IOException()
}
No mandatory handing in Kotlin but
We can if we wish same as Java with try-
catch
//Java side does the call, becomes checked and
forces handling
try {
MyKotlinTest().foo();
}
catch (IOException e) {
//…//
} finally {
//…//
}
56. Collection/Generics in Kotlin
Collection has Mutable and immutable interfaces that branches off into concrete
implementations. Map has a separate interface (like JDK MutableMap extending Map)
Generics are split into covariant/invariant/contravariant signatures using the out and in
keywords at declaration-site. No more extends/super spaghetti! (Use where for
multiple constraints).
Uses the JDK concrete implementations under the hood:
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
58. Generics Conversion
Invariant
Kotlin: <T>
Java: <T>
Example: Collection
concrete classes are
invariant
Covariant (Producer)
Kotlin: <out E>
Java: <? Extends E>
Upper bounded Example:
Collection interfaces are
covariant
Contravariant (Consumer)
Kotlin: <in E>
Java: <? Super E>
Lower bounded Example: Comparable
interface is contravariant (on declaration-
site in Kotlin)
Remember PECS = CIPO
Wildcard
<*> <out Any?>
<?> <? extends Object>
Wildcard example: iterate/read
through a list of any type
Note: No wildcards in Kotlin Generics!
59. Generics Conversion
Java:
//JSDK: public interface Comparable <T>
//Effective Java it. 28 recommends
//<T extends Comparable<? super T>>
//<T extends Comparable<T>>
<T extends Comparable<? super T>> void sortz(List<T> x){
sort(x);
}
Kotlin:
//KSDK: public interface Comparable<in T>
fun <T:Comparable<in T>> sortz(x :List<T>){
sort(x)
}
Remember PECS = CIPO
Kotlin:
fun processListz(l : MutableList<*>) {
l.add(22)
for(item in l) println(item)
}
val l = mutableListOf<Any>(123, "abc")
processListz(l)
Java:
void processListz(List<?> l) {
l.add(22)
for(Object item : l)
System.out.println(item);
}
/* ... */
List<Object> l = new ArrayList<Object>();
l.add(123);
l.add("abc");
processListz(l);
*Warning:(16, 25) Kotlin: Projection is redundant: the corresponding type parameter of Comparable has the same variance
60. Collection Generics at Declaration-site
JAVA
Some JDK Collection type Signatures (PECS at work!)
public interface Collection<E> extends Iterable<E> /* Java 8 */
/* ArrayList */
/*use-site covariance */
void addAll(Collection<? extends E> items);
KOTLIN
Some Kotlin Collection type Signatures (CIPO at work!)
/*declaration-site covariance */
public interface Collection<out E> : Iterable<E> /*covariant as to be expected */
/* MutableList */
public fun addAll(elements: Collection<E>): Boolean
Note: Generics are invariant in both Java and Kotlin, RAW types not implemented (no backwards compatibility needed)
61. Reified Generics
We can!
inline fun <reified T> f(a: Any) {
if (a is T) println("type matches!")
println("Hey! my class is ${T::class.java}!")
}
f<Int>(123)
We can’t!
inline fun <reified T> f() {
val x = T() /* T t = new T() */
}
f<SomeType>()
We can’t!
fun <T> g(a: Any) {
f<T>(a) // error, cant pass T
}
inline fun <reified T> f(a: Any) {}
We can!
inline fun <reified T> g(a: Any) {
f<T>(a) // OK, reified!
}
inline fun <reified T> f(a: Any) {}
These are not callable from Java code!
62. Collection in Kotlin R/O != immutable
Can’t be modified through the Collection<T>, Set<T> and List<T> interface methods (no mutable
methods exist on those interfaces!)
Some extension functions cast to a mutable type from an immutable implementation (according to JB)
and therefore Collection may be non thread-safe!
Using ‘as’ to cast to a MutableList succeeds but using a mutate operation (such as add) causes a
java.lang.UnsupportedOperationException
64. Streams-Like Processing (similar to streams in Java 8)
data class Student(
val name: String,
val surname: String,
val passing: Boolean,
val averageGrade: Double
)
studentz
.asSequence()
.filter { it.passing && it.averageGrade > 80.0 }
.sortedBy { it.averageGrade }
.take(2)
//.toList()
//.forEach { println(it.name) }
.forEach(::println)
Trivial example data filtering and extraction
val studentz = arrayListOf<Studentz>(
Studentz("edik","austin",false,70.0),
Studentz("lenka","penka",true,85.0),
Studentz("lasma","plasma",true,100.0),
Studentz("volodya","polodya",true,98.0)
)
Note: Can be viewed as a one-time
iterator on a Collection
65. Lambda Trivial HoF/Lambda example
//HOF
fun myHigherOrderFun(s:ArrayList<Studentz>,
f: (ArrayList<Studentz>)-> Pair<String,String> ) = f(s)
//ext fun on Sequence
fun <T> Sequence<T>.sequenceToTopNamePair() = this
.toList()
.let { Pair(it[0].toString(), it[1].toString()) }
//Lambda
myHigherOrderFun (studentz) {
it.asSequence() //make it lazy
.filter { it.passing && it.averageGrade > 80.0 }
.sortedByDescending { it.averageGrade }
.take(2)
.sequenceToTopNamePair()}
.let(::println)
66. Lambda
//SAM
Thread(Runnable {
/* actions */
}).start()
val r = Runnable { /* actions */ }
runz(r)
fun runz(f: (Runnable)) = Thread(f).start()
//Kotlin, no need for use-site variance, declaration-site has this!
fun <T : Comparable<in T>> dooz(a:T, b:T, p: (T,T) -> Boolean ) = p(a,b)
val p = { a: Number, b: Number -> (a > b) }
dooz(3.4f, 2.4f, p).let { println(it) } //true
val p2 = { a:Char, b:Char -> (a > b) }
dooz('a', 'b', p2).let(::println) //false
//another (completely different) way
fun <T:Comparable<in T>> T.dooz(b:T) = this > b
"a".dooz("b")
3.4f.dooz(2.4f)
studentz5.dooz(studentz4)
68. “ Kotlin deliberately has no
constructs for concurrency built
into the language. We believe
this should be handled by
libraries.
--Andrey Breslav
Kotlin Language Creator