ted@tedneward.com | Blog: http://blogs.tedneward.com | Twitter: tedneward | Github: tedneward | LinkedIn: tedneward
pick up some Kotlin syntax
understand Kotlin semantics
see some Kotlin idioms
walk away with an overview understanding of the language
What is Kotlin?
From the website:
"Statically typed programming language for the JVM, Android and the browser"
From Wikipedia:
"Kotlin is a statically-typed programming language that runs on the Java Virtual Machine and also can be compiled to JavaScript source code"
Kotlin is...
statically typed
object-oriented
compiled
cross-targeting
JVM
Android (Dalvik, ART)
JavaScript
Installation
IntelliJ has a plugin
Command-line installs
Eclipse plugin
Installation: IntelliJ
(nothing to do!)
Installation: Command-line
Manual install:
Download the package, unzip it
Make sure Kotlin bin/ directory is on the PATH
SDKMAN!
Use SDKMAN! to handle installs (any *nix system)
$ curl -s https://get.sdkman.io | bash
$ sdk install kotlin
OSX Homebrew
$ brew install kotlin
Hello, Kotlin
fun main(args: Array<String>) { println("Hello, World!") }
Compiling the standalone executable (command-line)
$ kotlinc hello.kt -include-runtime -d hello.jar $ java -jar hello.jar Hello World! $
(This produces a "fat JAR"--includes all the Kotlin dependencies)
Compiling the standalone executable (command-line)
$ kotlinc hello.kt
(This produces HelloKt.class in the current directory)
$ kotlin HelloKt Hello World! $
(This uses the JVM to run HelloKt.)
Running kotlinc as a scripting tool
Code expected to start execution on line 1 (hello.kts):
println("Hello, World!")
$ kotlinc -script hello.kts
Lastly, Kotlin can be run as a REPL
run "kotlinc-jvm"
Kotlin can also compile to JavaScript
use "kotlinc-js" to do the transformation
compile either "executables" or "libraries"
libraries can be distributed and consumed as JAR files
Syntax: C-family language based (loosely)
curly-brackets for scope
alpha/alphanumeric identifiers
/* */ multiline comments
// end-of-line comments
optional semicolon line terminators
Source files may begin with "package" declarations
indicates lexical namespace for all types found within
the unnamed/unspecified package is called "default"
no relationship between package name and filesystem state is assumed
allows for flexible packaging conventions
Source files may reference other modules
use "import" to bring in top-level lexical elements
import foo.Bar
makes "Bar" accessible without qualification
import foo.*
makes all foo elements accessible without qualification
import foo.Bar as bBar
makes foo.Bar accessible as bBar
Primitive types
"everything is an object"
val
denotes immutable value declaration
var
denotes mutable variable declaration
"destructuring" declarations permitted
if the instance being destructured supports this
suffixed ("Pascal-style") type descriptors
Kotlin will infer type if type descriptor is absent
Nullable type declared with "?" suffix
Boolean
true
, false
: literals
logical operations: ||
, &&
, !
Numbers
Integral Numbers: Long
(64), Int
(32), Short
(16), Byte
(8)
integral literals are Int by default
Long literals require an "L" suffix
Floating-point Numbers: Double (64), Float(32)
floating-point literals are Double by default
Float literals require an "F" or "f" suffix
Hexadecimal constants: 0x prefix; Binary constants: 0b prefix
Numbers
No implicit conversion (except for literals)
Explicit conversion required for widening or narrowing conversions
All numeric types support conversion methods:
toByte
, toShort
, toInt
, toLong
toFloat
, toDouble
, toChar
Kotlin conversions
val a: Int = 10000 print(a === a) // Prints 'true' val boxedA: Int? = a val anotherBoxedA: Int? = a print(boxedA === anotherBoxedA) // !!!Prints 'false'!!! val b: Byte = 1 // OK, literals are checked statically // val i: Int = b // ERROR val i: Int = b.toInt() // OK: explicitly widened
Char: single character
single-quote literals
backslash-escaped: \t \b \n \r ' " \ $
Unicode escape sequence syntax: '\uFF00'
Strings: multiple-character
double-quite literals
triple-quote "heredoc" strings
individual elements accessed using ""
elements can be iterated using "for" loop
supports "${expression}" interpolation
Kotlin strings
val s = "Hello, world!\n" val s2 = "s = ${s}!" val text = """ for (c in "foo") print(c) """ val quote = """ |Tell me and I forget. |Teach me and I remember. |Involve me and I learn. | (Benjamin Franklin) """.trimMargin()
Arrays
represented by Array class
fixed-size
provides size
, []
access and iteration
construct using arrayOf
or arrayOfNulls
, or factory function
specialized intArrayOf
for each primitive type available
Kotlin arrays
val nums = arrayOf(1, 2, 3) // [1, 2, 3] println(nums[0]) //nums[1] = 12 // ERROR! println(nums[2]) val nulls : Array<Any?> = arrayOfNulls(3) // [null, null, null] val asc = Array(5, { i -> (i * i).toString() }) // [0,1,4,9,16] println(asc)
Ranges
finite sequence
formed using rangeTo
or ..
operator
membership tested using in
or !in
operators
supports iteration
Kotlin ranges
val nums = 0..10 // 0,1,2,3,...,10 if (1 in nums) println("true!") val desc = 10 downTo 0 // 10,9,8,...,0 val evens = 0..10 step 2 // 0,2,4,...,10 if (5 in evens) println("Errr... broken?") val odds = 1..10 step 2 // 1,3,5,...,9 if (5 in odds) println("true!")
Lists
ordered collection
supports mutable and immutable
construct using mutableListOf()
or listOf()
Kotlin lists
val items = listOf(1, 2, 3, 4) items.first() == 1 items.last() == 4 val evens = items.filter { it % 2 == 0 } println(evens == listOf(2, 4)) // true val rwList = mutableListOf(1, 2, 3, 4) if (rwList.none { it > 6}) println("No item in rwList is greater than 6")
Sets
unordered collection, guarantees uniqueness
supports mutable and immutable
construct using mutableSetOf()
or setOf()
Kotlin sets
val numbers = hashSetOf(0, 1, 2, 2, 3, 3, 3, 4, 5, 4) println(numbers) // [0, 1, 2, 3, 4, 5]
Maps
unordered collection of key/value pairs
supports mutable and immutable
construct using hashMapOf(key to value, ...)
Kotlin maps
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2) println(readWriteMap["foo"]) // prints "1" val snapshot: Map<String, Int> = HashMap(readWriteMap) println(snapshot) //snapshot["foo"] = 12 // ERROR!
if
(decision-branching)
true
/false
expression evaluation
itself is an expression (yields a value)
each branch can be a single expression or a block
if a block, last value is assumed value for the block
Kotlin if
var a = 5 var b = 12 var max: Int = 0 if (a > b) max = a else max = b // As expression val max2 = if (a > b) a else b
when
(decision-branching)
replaces 'switch' from C-family languages
matches expression sequentially against branches until a match is found
else
branch is used if no match is found
can be either statement or expression
Kotlin when
val x = 2 when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { // Note the block print("x is neither 1 nor 2") } }
when branch conditions
can be combined with a comma
can be arbitrary expressions
can be in a range or collection
can check for type using "is"
Kotlin when
val validNumbers = 1..10 when (x) { 0, 1 -> print("x == 0 or x == 1") parseInt(s) -> print("s encodes x") in 1..10 -> print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("otherwise") } val hasPrefix = when(y) { is String -> y.startsWith("prefix") else -> false }
for
(iteration/loops)
iterates through anything that provides an iterator
body can be either single expression or block
note that different collections permit different iteration options
Kotlin for
val array = arrayOf(1, 2, 3, 4, 5, 6) for (i in array) println(i) for (idx : Int in array) { println(idx) } for (index in array.indices) { println(array[index]) } for ((index,value) in array.withIndex()) { println("the array element at $index holds $value") }
while
, do
/while
(loops)
each takes statement body for repeated execution
while evaluates expression before evaluation of body
do/while evaluates expression after evaluation of body
break
terminates nearest enclosing loop
continue
proceeds to next step of nearest enclosing loop
return
returns from nearest enclosing function
Kotlin manages errors as Exceptions
(very much in line with the JVM)
exception objects are thrown "up the stack"
exception objects can be caught and handled or re-thrown
Throwing an exception is simple
"throw (instance)"
type must derive (ultimately) from Throwable
types usually imply some level of detail
Kotlin does not follow Java standard of "checked/unchecked" exceptions; all exceptions are "unchecked"
Guarded blocks frame exception-handling behavior
try
indicates guarded block
body of try indicates area of code guarded
guarded block must have 1+ catch
clauses and/or finally
clause
try
block is itself an expression (yields a value)
Kotlin Exceptions
val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }
Functions
fun
keyword
optional identifier name (none = anonymous function literal)
parenthesized parameter list
parameter declarations can have default values
vararg-declared parameters are an Array-of-T parameter
return type descriptor
no return means return type is Unit or can be omitted
expression or block
single-expression functions can omit the curly brackets and return type
Invoking Functions
parenthesized invocation syntax
type/number of parameters must match
parameter names can be given at point of call to replace default-valued parameters
note that if last parameter to a function is a function, a lambda can be passed outside of the parentheses
Function declarations
fun add(left: Int, right: Int): Int { return left + right } val five = add(2, 3) val adder = fun(left: Int, right: Int): Int { return left + right } val six = adder(3, 3) val adderUp = fun(left: Int, right: Int): Int = left + right
Function parameter variations
fun doComplicatedThings(a: Int = 0, b: Int = -1, c: Boolean = false): Int { return if (c) a else b } doComplicatedThings() // = -1 doComplicatedThings(c = true) // = 0 fun log(msg: String, vararg msgs: String): Unit { print(msg) for (m in msgs) { print(" " + m) } println() } log("Howdy!") log("Howdy","y'all!")
Passing Functions
non-parenthesized reference syntax
function parameter list (type/number) and return type must match
function type is marked as (T1, T2, ...) -> RT
--- where T1, T2, ... are parameter types and RT is return type
Shorthand (lambda) function syntax
parenthesized parameter names (no types)
singular parameters can be defined without parens
singular parameters can be omitted entirely using default name "it"
->
body
Lambdas
val ints = arrayOf(4, 8, 16) val doubled = ints.map { it -> it * 2 } val halved = ints.map { it / 2 } fun doSomethingNTimes(times: Int, body: () -> Unit) { for (i in 1..times) { body() } } doSomethingNTimes(5) { println("Whee!") }
class
followed by identifier
primary constructor appears after identifier
includes parameter list
named parameters can be referenced inside class body
val
or var
declared parameters declare immutable or mutable properties
parameters can have default values and/or varargs, just as with functions
initializer blocks (init
) in class body are executed on construction
Instantiating a class
use class type as "constructor function" (no "new")
constructor parameter list (type/number) must be satisfied
Simple class and use
class Person(var firstName: String, val lastName: String, var age: Int) { init { println("Person $firstName $lastName constructed") } } val ted = Person("Ted", "Neward", 45) println("${ted.firstName} is ${ted.age} years old")
Classes can declare secondary constructors
must delegate to primary constructor using ": this(params)" after declaration
body is then able to provide whatever logic/behavior desired
Secondary constructors
class Person(var firstName: String, val lastName: String, var age: Int) { constructor(soleName: String, age: Int) : this(soleName, "", age) { } } val ted = Person("Ted", "Neward", 45) println("${ted.firstName} is ${ted.age} years old") val cher = Person("Cher", 39) println("${cher.firstName} is ${cher.age} years old")
Classes can contain
secondary constructors
properties
functions (methods)
object declarations (statics)
nested/inner classes
nested classes declared with "inner" have access to private members of enclosing class
Properties represent state in a class
properties can be read-only (val) or mutable (var)
properties can be initialized with values
type declaration is optional if initializer is present
properties can have optional getter/setter
get()
returns a value of the correct type
set(value)
provides for validation concerns
both are functions (with all the syntactic shortcuts)
backing fields referenced via "field" keyword
Simple property usage
public class Address { public var name: String = "" public var street: String = "" public var city: String = "" public var state: String? = "" public var zip: String = "" } val addr = Address() addr.name = "Fred Flintstone" addr.street = "1 Bedrock Way"
Getter/setter usage
class SecretAsset { var codeName: String = "" var realName: String = "" get() = "<REDACTED>" set(value) { field = value } }
Sometimes the implicit backing field isn't quite right
define a "backing property"
operates/behaves basically like languages without properties at this point
Backing properties
class Container { private var _table: Map<String, Int>? = null public val table: Map<String, Int> get() { if (_table == null) _table = HashMap() return _table ?: throw AssertionError("Set to null by another thread") } }
Properties can be marked "const"
for compile-time constants only
must be top-level in a package or as part of an object
must be initialized with String or primitive type
no custom getter
Properties can be marked "lazyinit"
only available on var properties
cannot have custom getter/setter
not exactly the same as "initialized on first use"; just implies "initialization will happen later"
Delegated properties
certain kinds of behavior around properies are common
lazy properties
observable properties
backing store as a map, not fields
it would be nice to be able to not re-write these each time
enter "delegating properties"
Using a delegating property
class Example { var p: String by Delegate() }
Delegated type must expose two operator methods
getValue()
setValue()
A delegating property type
class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name} in $thisRef.'") } }
Out-of-the-box property delegate types
lazy: lambda fired on first access
Delegates.observable: registers lambdas for later access
map: store property data in a Map
lazy
class Container { val lazyValue: String by lazy { println("computed!") "Hello" } } val c = Container() println(c.lazyValue) println(c.lazyValue)
observable
class User { var name: String by Delegates.observable("<no name>") { prop, old, new -> println("$old -> $new") } } val user = User() user.name = "first" user.name = "second"
map
class Person(val map: Map<String, Any?>) { val name: String by map val age: Int by map } val person = Person(mapOf( "name" to "John Doe", "age" to 25 )) println(person.name) println(person.age) println(person.map)
Classes can have behavior
expressed either as methods (named) or operators (symbolic or inferred)
both are essentially functions in character
Methods
class FootballPlayer { fun run() { println("Running!") } fun pass() { println("Passing!") } } val tomBrady = FootballPlayer() tomBrady.pass() tomBrady.pass() //tomBrady.cheat() // No! Bad Tom!
Methods can be marked with "operator"
operator overloads are methods by fixed names
unary operations
"+a" == "unaryPlus()"
"-a" == "unaryMinus()"
"!a" == "not()"
"a++" == "inc()" (shouldn't modify object)
"a--" == "dec()" (shouldn't modify object)
Methods can be marked with "operator"
binary operations
"a + b" == "plus()"
"a - b" == "minus()"
"a * b" == "times()"
"a / b" == "div()"
"a % b" == "mod()"
"a..b" == "rangeTo()"
"a in b" == "b.contains()"
"a !in b" == "b.contains()" (inversed)
Methods can be marked with "operator"
subscript operations
"ai" == "a.get(i)"
"ai, j" == "a.get(i, j)"
"ai_1, ..., i_n" == "a.get(i_1, ..., i_n)"
"ai = b" == "a.set(i, b)"
"ai, j = b" == "a.set(i, j, b)"
"ai_1, ..., i_n = b" == "a.set(i_1, ..., i_n, b)"
Methods can be marked with "operator"
invocation operations
"a()" == "a.invoke()"
"a(i)" == "a.invoke(i)"
"a(i, j)" == "a.invoke(i, j)"
"a(i_1, ..., i_n)" == "a.invoke(i_1, ..., i_n)"
Operators
data class Money(val amount: Int, val currency: String = "USD") { operator fun plus(other: Money): Money { if (this.currency != other.currency) throw Exception("Conversion required!") else return Money(this.amount + other.amount, this.currency) } } operator fun Money.unaryMinus() = Money(-amount, currency) val salary = Money(100000, "USD") val bonus = Money(50000, "USD") println(salary + bonus)
Member functions can be used with infix notation
mark method with "infix"
name need not be an operator
typically must return a value
Infix operations
class Matrix(var number : Int) { infix fun rotate(amt: Int): Matrix { return this } } var m = Matrix(5) m = m rotate 5
Object declarations
pseudo-replacement for static members/fields/methods
use "object" instead of "class" to create Singleton instance
objects can have supertypes if desired
use object identifier name to reference instance
Objects
object DataProviderManager { fun registerDataProvider(provider: DataProvider) { // ... } val allDataProviders: Collection<DataProvider> get() = // ... } DataProviderManager.registerDataProvider(...)
Companion objects can be declared using "companion" keyword
companion object declared/defined inside its partner
methods on companion object are "as if" they were declared on the enclosing class
name can be omitted (in which case the companion is called "Companion")
Companion objects
class MyClass { companion object Factory { fun create(): MyClass = MyClass() } } val instance = MyClass.create() class MyOtherClass { companion object { } } val companionInstance = MyOtherClass.Companion
Access consists of four modifiers:
public (default if nothing specified)
private
internal
protected
Package-level declarations
public: accessible everywhere
private: only visible inside the file
internal: visible to entire module
protected: N/A
Class declarations
members declared (X) are...
public: visible everywhere
private: visible in this class alone
internal: visible in this class and the rest of the module
protected: visible in this class and subclasses
Kotlin supports single-class Inheritance
meaning one base implementation class
same rules as most O-O post-C++ languages
base class must be declared "open"
meaning, it must be declared ready for inheritance
base class appears after class name and colon
after primary constructor parameter list
base class primary constructor parameters passed here
if the base has no primary, secondary ctors must use super()
Simple inheritance
open class Base(p: Int) class Derived(p: Int) : Base(p)
All classes implicitly extend base type "Any"
this is not Object
it provides a much smaller interface
toString()
equals()
hashCode()
Type-testing/casting
"A is B" operator tests A to see if it is type-compatible with B
yields true/false value
"!is" yields inverse value
"smart check"; Kotlin can infer that if "A is B", "A" should be cast to B
"smart casts" apply to if, when and while expressions (and a few other places)
"unsafe" cast operator "as"
throws exception if the cast is not safe
null will not cast as per Java rules; nullable type ("?") comes into play
"safe" cast operator "as?"
yields correctly-cast object on success
yields null on failure
Property overriding
like base classes, base properties must be declared "open"
"override" can also be used in the primary constructor of the derived
Overriding properties
open class Person(val firstName: String, val lastName: String, val age: Int) { open var salary: Int = 50000 } class Actor(val name: String, var oscars: Int) : Person(name, "", 39) { override var salary: Int get() { return 1000000 } set(value) { /* ignored */ } } val willSmith = Actor("Will", 0) println(willSmith.salary)
Method overriding
like base classes, base methods must be declared "open"
derived method must be declared "override"
override methods are implicitly "open"
to prevent further overrides, use "final"
Overriding properties
open class Base { open fun v() { } fun nv() { } } class Derived() : Base() { override fun v() { } }
Classes and/or members can be abstract
cannot be instantiated
must be inherited
abstract implies "open"
Delegation
Kotlin will allow a class to delegate method calls to owned instance
accept an instance of an interface implementation as a member
implement an interface "by" the member
Delegation
interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() { print(x) } } class Derived(b: Base) : Base by b fun main(args: Array<String>) { val b = BaseImpl(10) Derived(b).print() // prints 10 }
Anonymous object derivative instances ("object expressions")
use "object" keyword, then inherit from desired base type and/or interfaces
constructor obligations must be fulfilled as normal
can access variables from enclosing outer scope
Object expressions
open class A(x: Int) { public open val y: Int = x } interface B { } val ab: A = object : A(1), B { override val y = 15 }
Object expressions can also create "ad-hoc" objects
simply declare "object" and describe contents
unknown type, one-off declaration
Ad-hoc object expressions
val adHoc = object { var x: Int = 0 var y: Int = 0 } print(adHoc.x + adHoc.y)
Interfaces are behavior-only markers
interfaces can have no state
interfaces "promise" behavior on the part of implementors
Kotlin interfaces can also carry default behavior
A simple interface
interface MyInterface { fun bar() fun foo() { // optional body } }
Classes inherit from interfaces
any number of interfaces allowed
interface methods are all implicitly "open"
if member names clash, use "super" to resolve
only necessary when two interfaces implemented on a class have same method and both have default implementations
Interfaces can contain properties
these can either be abstract or have default getter implementation
interfaces cannot have backing properties/fields
Interface with properties
interface MyInterface { val property: Int // abstract val propertyWithImplementation: String get() = "foo" fun foo() { print(property) } } class MyClass : MyInterface { override val property: Int = 29 }
Some classes just want to be named bundles of data
Kotlin calls these "data classes"
use the "data class" keyword
must follow the following requirements
primary constructor must have 1+ parameters
all primary constructor parameters must be val/var-declared
cannot be abstract, sealed, open or inner
may not extend other classes (but may implement interfaces)
Kotlin can generate numerous support methods/properties automatically
Data classes out-of-the-box support:
equals()
/hashCode()
toString()
destructuring declaration support
copy()
(cloning with named parameter replacements)
Data classes
data class Money(var amount: Int, val type: String) val salary = Money(100000, "USD") println("Your salary is ${salary.type}${salary.amount}") val euroSalary = salary.copy(type = "EUR") println("In Europe, you would make ${euroSalary}")
Basic usage
type-safe bounded list of possible values
"enum class" followed by list of values (typically all-capped)
Basic enums
enum class Direction { NORTH, SOUTH, EAST, WEST } val heading = Direction.NORTH println(heading)
Enums can also be initialized with values
defined in enumeration typename
parameter name available as property on enum instance
Valued enums
enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF) } val r = Color.RED println(r.rgb)
Enums can also declare anonymous (sub) classes
provides limited override capabilities
great for state machines
Anonymous subclass enums
enum class ProtocolState { WAITING { override fun signal() = TALKING }, TALKING { override fun signal() = WAITING }; abstract fun signal(): ProtocolState } var state = ProtocolState.WAITING println(state) // WAITING state = state.signal() println(state) // TALKING
Sealed classes are a closed inheritance family
many similarities to enumerated types, but full-blown class/subclasses
also known as "discriminated unions" in some languages
Sealed classes start with base class marked "sealed"
all derived classes appear within sealed class body
subclasses of derived don't necessarily have to (but probably should)
A sealed expression library
sealed class Expr { class Const(val number: Double) : Expr() class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr() } fun eval(expr: Expr): Double = when(expr) { is Expr.Const -> expr.number is Expr.Sum -> eval(expr.e1) + eval(expr.e2) Expr.NotANumber -> Double.NaN // the `else` clause is not required because we've covered all the cases } val expr = Expr.Sum(Expr.Const(2.0), Expr.Const(2.0)) println("${expr.e1} + ${expr.e2} = ${eval(expr)}") // 4.0
Kotlin supports parameterized types
type parameter specified in angle brackets
type parameters are part of signature
must be supplied (or inferred) at construction
Simple generics example
class Box<T>(t: T) { var value = t } val box1: Box<Int> = Box<Int>(1) val box2 = Box(1) // 1 is-a Int, so box2 must be a Box<Int>
Functions can also have type parameters
declare the type parameter before the function
multiple generic parameter types require multiple type parameters
Simple generic functions
fun <T> singletonList(item: T): List<T> { // ... } fun <T,U> swap(params: Pair<T,U> ): Pair<U,T> { return Pair(params.second, params.first) }
Annotations are metadata for code
usage is @
-prefixed (a la Java)
can be customized to particular class elements if necessary
definition uses annotation
keyword before a class
some restrictions, but mostly minimal
Usage
declare before target element
for primary constructor, use "@Annotation constructor" syntax in class declaration line
for property setters, declare annotation before "get/set" keyword
function literals can also have annotations
declare right before opening bracket
when using annotations, sometimes more precise declaration usage is required
these are called "Use-site targets"
colon-prefixed "keyword" before the annotation type name
Use-site target list: file, property, field, get, set, receiver, param, setparam, delegate
Annotation definition
@Fancy class Foo { @Fancy fun baz(@Fancy foo: Int): Int { return (@Fancy 1) } } class InFoo @Inject constructor(dependency: MyDependency) { // ... var x: MyDependency? = null @Inject set } @Special("Because it's a Foo too!") class FooTwo {} val f = @Suspendable { Fiber.sleep(10) }
Annotation definition
@file:JvmName("Example") class Example(@field:Ann val foo, // annotate Java field @get:Ann val bar, // annotate Java getter @param:Ann val quux) // annotate Java constructor parameter
Definition
use annotation
instead of class
additional annotations can specify annotation attributes
@Target
: which elements can this annotation apply to
@Retention
: where is the annotation value stored
@Repeatable
: allows annotation to be used multiple times
@MustBeDocumented
: include annotation in generated documentation
annotations may have constructors
parameter types restricted to primitives, strings, classes, enums, other annotations and arrays of any of the previous list
these are turned into annotation properties
Annotation definition
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented annotation class Fancy annotation class Special(val why: String)
Kotlin Reflection is access to code elements by name without invocation
uses ::{name}
syntax to reference code elements
class
, get()/set()
, code element by name, and so on
particularly useful where function objects are expected
functional-style programming idioms
Function composition in Kotlin
fun isOdd(x: Int) = x % 2 != 0 fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove" val numbers = listOf(1, 2, 3) println(numbers.filter(::isOdd)) // prints [1, 3] // since numbers is a list of Int, the IsOdd(Int) is selected // Function composition fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C { return { x -> f(g(x)) } } fun length(s: String) = s.length val oddLength = compose(::isOdd, ::length) val strings = listOf("a", "ab", "abc") println(strings.filter(oddLength)) // Prints "[a, abc]"
Dynamic is a type that represents a dynamic type
in essence, it turns off the static type checker
unavailable when targeting the JVM
useful for interoperation with dynamic runtime target environments
for now, that means "ECMAScript"
Dynamic is a type
dynamic values can be assigned to any variable
dynamic values can be passed for any parameter
any value can be assigned to a dynamic instance
any value can be passed for a dynamic parameter
null checks are disabled for dynamics
dynamic calls always return dynamic
Kotlin is...
an object-oriented language
with some functional features/primitives
and a dash of some syntactic flexibility
but overall, a relatively easy language to approach, understand, and use
Who is this guy?
Architect, Engineering Manager/Leader, "force multiplier"
Principal -- Neward & Associates
http://www.newardassociates.com
Educative (http://educative.io) Author
Performance Management for Engineering Managers
Author
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)
Effective Enterprise Java (Addison-Wesley, 2004)
SSCLI Essentials (w/Stutz, et al; OReilly, 2003)
Server-Based Java Programming (Manning, 2000)