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
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!!!
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
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.
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
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.
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!
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!!) }
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 */ } }
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
{ 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!
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
<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.
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!
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
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 }
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
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.
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
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
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
} 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() }
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
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.
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");
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 { //…// }
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>
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!
(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)
{ 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!
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
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
= 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)