"Statically typed programming language for modern multiplatform applications"https://kotlinlang.org/
JVM
Android
JavaScript
Native
fun main(args: Array<String>) {
println("Hello, world!")
}
println
instead of System.out.println
fun main(args: Array<String>) {
val name = if(args.size > 0) args[0] else "world"
println("Hello, $name!")
}
val
for immutable variablesif
and when
are expressionsfun main(args: Array<String>) {
val name = if(args.size > 0) args[0] else "world"
val sender = if(args.size > 1) args[1] else "not important"
println("""Hello, $name,
|My name is $sender.
|I need your help.
""".trimMargin())
}
fun main(args: Array<String>) {
val name = if(args.size > 0) args[0] else "world"
var sender = "not important"
if(args.size > 1) sender = args[1]
println("""Hello, $name,
|My name is $sender.
|I need your help.
""".trimMargin())
}
var
for mutable variablesfor(i in 1 .. 10) { // 1 2 3 4 5 6 7 8 9 10
print(" $i")
}
for(i in 1 until 10) { // 1 2 3 4 5 6 7 8 9
print(" $i")
}
for(i in 10 downTo 1 step 2) { // 10 8 6 4 2
print(" $i")
}
var j = 0
while(j++ < 5) print(" $j") // 1 2 3 4 5
var k = 0
do print(" $k") while(k++ < 5) // 0 1 2 3 4 5
fun display(input: Any) {
when(input) {
10 -> println("ten")
11, 12 -> println("eleven or twelve")
in 1 .. 9 -> println("a single-digit number")
is String -> println("a string with length ${input.length}")
else -> println("something else")
}
}
...
...
...
display(10) // ten
display(12) // eleven or twelve
display(5) // a single-digit number
display("Hello") // a string with length 5
display(42) // something else
display(RuntimeException()) // something else
fun isPalindrome(s: String): Boolean {
return s == s.reversed()
}
fun main(args: Array<String>) {
println(isPalindrome("dehydrated")) // false
println(isPalindrome("detartrated")) // true
}
==
calls equals()
===
compares referencesfun isPalindrome(s: String) = s == s.reversed()
fun main(args: Array<String>) {
println(isPalindrome("dehydrated")) // false
println(isPalindrome("detartrated")) // true
}
fun String.isPalindrome() = this == this.reversed()
fun main(args: Array<String>) {
println("dehydrated".isPalindrome()) // false
println("detartrated".isPalindrome()) // true
}
import java.util.*
import kotlin.math.PI
fun write(value: Double, decimals: Int,
prefix: String, suffix: String, locale: Locale) {
println("${prefix}%.${decimals}f${suffix}".format(locale, value))
}
fun main(args: Array<String>) {
write(PI, 5, "length = ",
" cm.", Locale.GERMANY) // length = 3,14159 cm.
write(PI, 4, "", " cm.", Locale.US) // 3.1416 cm.
write(PI, 2, "", "", Locale.GERMANY) // 3,14
write(PI, 2, "", "", Locale.US) // 3.14
}
import java.util.*
import kotlin.math.PI
fun write(value: Double, decimals: Int = 2,
prefix: String = "", suffix: String = "", locale: Locale = Locale.US) {
println("${prefix}%.${decimals}f${suffix}".format(locale, value))
}
fun main(args: Array<String>) {
write(PI, 5, "length = ",
" cm.", Locale.GERMANY) // length = 3,14159 cm.
write(PI, 4, "", " cm.") // 3.1416 cm.
write(PI, 2, "", "", Locale.GERMANY) // 3,14
write(PI) // 3.14
}
import java.util.*
import kotlin.math.PI
fun write(value: Double, decimals: Int = 2,
prefix: String = "", suffix: String = "", locale: Locale = Locale.US) {
println("${prefix}%.${decimals}f${suffix}".format(locale, value))
}
fun main(args: Array<String>) {
write(PI, locale = Locale.GERMANY, prefix = "length = ",
suffix = " cm.", decimals = 5) // length = 3,14159 cm.
write(PI, decimals = 4, suffix = " cm.") // 3.1416 cm.
write(PI, locale = Locale.GERMANY) // 3,14
write(PI) // 3.14
}
Modifier | Corresponding member | Comments |
---|---|---|
final |
Can’t be overridden |
Used by default for class members |
open |
Can be overridden |
Should be specified explicitly |
abstract |
Must be overridden |
Can be used only in abstract classes; abstract members can’t have an implementation |
override |
Overrides a member in a superclass or interface |
Overridden member is open by default, if not marked final |
Modifier | Class member | Top-level declaration |
---|---|---|
public (default) |
Visible everywhere |
Visible everywhere |
internal |
Visible in a module |
Visible in a module |
protected |
Visible in subclasses |
- |
private |
Visible in a class |
Visible in a file |
public class Person {
private final String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Person(val name: String, var age: Int)
class Person(val name: String, var age: Int)
fun main(args: Array<String>) {
val p = Person("Jim", 33)
println(p.name)
}
Person
is a value objectpublic
and final
by defaultnew
keywordname
and age
are propertiesclass Person(val name: String, var age: Int) {
val isAdult: Boolean
get() = age >= 18
}
fun main(args: Array<String>) {
val p = Person("Jim", 33)
println(p.isAdult) // true
p.age = 15
println(p.isAdult) // false
}
class Person(val name: String, var age: Int) {
var jobs = 0
set(value) {
field = when {
value < 0 -> 0
value > 3 -> 3
else -> value
}
}
}
fun main(args: Array<String>) {
val p = Person("Jim", 33)
p.jobs = 1
println(p.jobs) // 1
p.jobs = -5
println(p.jobs) // 0
p.jobs = 7
println(p.jobs) // 3
}
field
to access the backing fieldpublic class Person {
private final String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public Person copy() {
return new Person(name, age);
}
@Override
public String toString() {
return "Person(name=" + name + ", age=" + age + ")";
}
}
data class Person(val name: String, var age: Int)
data class Person(val name: String, var age: Int)
fun main(args: Array<String>) {
val p1 = Person("Jim", 33)
val p2 = p1.copy()
println(p1) // Person(name=Jim, age=33)
println(p2) // Person(name=Jim, age=33)
println(p1 == p2) // true
println(p1 === p2) // false
val p3 = p1.copy(age=17)
println(p3) // Person(name=Jim, age=17)
}
Class A declared within another class B | In Java | In Kotlin |
---|---|---|
Nested class (doesn’t store a reference to an outer class) |
|
|
Inner class (stores a reference to an outer class) |
|
|
sealed class Alert
class Email(val recipient: String, val title: String, val body: String) : Alert()
class SMS(val cc: Int, val ndc: Int, val sn: Int, val message: String) : Alert()
fun getAckMessage(alert: Alert) =
when(alert) {
is Email -> "Email with title '${alert.title}' sent to ${alert.recipient}: ${alert.body}"
is SMS -> "SMS sent to ++(${alert.cc})${alert.ndc}-${alert.sn}: ${alert.message}"
}
fun main(args: Array<String>) {
println(getAckMessage(Email("nobody@nowhere.net", "Epic fail", "Your server exploded")))
// Email with title 'Epic fail' sent to nobody@nowhere.net: Your server exploded
println(getAckMessage(SMS(49, 171, 47110815, "Godzilla is back in town")))
// SMS sent to ++(49)171-47110815: Godzilla is back in town
}
open class User(val nickname: String) // primary constructor
class TwitterUser(nickname: String) : User(nickname) // calling parent's primary constructor
open class Button // a default constructor is generated
class RadioButton: Button() // calling parent's default constructor
class MenuItem(val name: String,
var enabled: Boolean=true,
var checked: Boolean=false) // constructor with default arguments
class Secretive private constructor() // private primary constructor
class Point(val x: Int, val y: Int)
class Rectangle(val x1: Int, val y1: Int, val x2: Int, val y2: Int) { // primary constructor
constructor(p1: Point, p2: Point): this(p1.x, p1.y, p2.x, p2.y) // secondary constructor
}
class Point(val x: Int, val y: Int)
class Rectangle { // no primary constructor
val x1: Int
val y1: Int
val x2: Int
val y2: Int
constructor(p1: Point, p2: Point) { // secondary constructor
x1 = p1.x
y1 = p1.y
x2 = p2.x
y2 = p2.y
}
}
class Point(val x: Int, val y: Int)
class Rectangle(p1: Point, p2: Point) { // primary constructor
val x1: Int
val y1: Int
val x2: Int
val y2: Int
init { // initializer block
x1 = p1.x
y1 = p1.y
x2 = p2.x
y2 = p2.y
}
}
class MyLogger {
val context = mutableMapOf<String,String>()
fun log(message: String) {
println("$context: $message")
}
}
val Logger = MyLogger()
fun main(args: Array<String>) {
Logger.context["thread"] = "worker-7"
Logger.context["txID"] = "dead-b055-1234"
Logger.log("Account successfully deleted")
// Output: {thread=worker-7, txID=dead-b055-1234}: Account successfully deleted
}
static
keyword in KotlinLogger
is a top-level propertyobject Logger {
val context = mutableMapOf<String,String>()
fun log(message: String) {
println("$context: $message")
}
}
fun main(args: Array<String>) {
Logger.context["thread"] = "worker-7"
Logger.context["txID"] = "dead-b055-1234"
Logger.log("Account successfully deleted")
// Output: {thread=worker-7, txID=dead-b055-1234}: Account successfully deleted
}
object
provides support for creating singletonsdata class Person(val name: String) {
object NameComparator : Comparator<Person> {
override fun compare(p1: Person, p2: Person) = p1.name.compareTo(p2.name)
}
}
fun main(args: Array<String>) {
val persons = listOf(Person("Bob"), Person("Alice"))
println(persons.sortedWith(Person.NameComparator))
// Output: [Person(name=Alice), Person(name=Bob)]
}
object
can appear inside classesobject
declarations can inherit from classes and interfacesimport kotlin.math.*
data class Point (val x: Double, val y: Double)
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
fun main(args: Array<String>) {
println(Point(3.0, 4.0))
println(fromPolar(5.0, PI / 3))
}
fromPolar
should belong to Point
import kotlin.math.*
data class Point private constructor(val x: Double, val y: Double) {
object Factory {
fun fromCartesian(x: Double, y: Double) = Point(x, y)
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
}
}
fun main(args: Array<String>) {
println(Point.Factory.fromCartesian(3.0, 4.0))
println(Point.Factory.fromPolar(5.0, PI / 3))
}
Factory
)?import kotlin.math.*
data class Point private constructor(val x: Double, val y: Double) {
companion object {
fun fromCartesian(x: Double, y: Double) = Point(x, y)
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
}
}
fun main(args: Array<String>) {
println(Point.fromCartesian(3.0, 4.0))
println(Point.fromPolar(5.0, PI / 3))
}
companion
object per classfun getEmailUser(email: String): String {
val user =
if(email.contains('@'))
email.substringBefore('@')
else
throw IllegalArgumentException()
return user.toLowerCase()
}
fun main(args: Array<String>) {
println(getEmailUser("John.Smith@example.org")) // john.smith
println(getEmailUser("I have no email")) // throws IllegalArgumentException
}
fun getEmailUser(email: String): String {
val user = if(email.contains('@')) email.substringBefore('@')
else throw IllegalArgumentException()
return user.toLowerCase()
}
fun printEmailUser(email: String) {
val user = try {
getEmailUser(email)
} catch(e: Exception) {
return
}
println(user)
}
fun main(args: Array<String>) {
printEmailUser("John.Smith@example.org") // john.smith
printEmailUser("I have no email") // nothing is printed
}
fun getEmailUser(email: String): String {
val user = if(email.contains('@')) email.substringBefore('@')
else throw IllegalArgumentException()
return user.toLowerCase()
}
fun printEmailUser(email: String) {
val user = try {
getEmailUser(email)
} catch(e: Exception) {
"unknown"
}
println(user)
}
fun main(args: Array<String>) {
printEmailUser("John.Smith@example.org") // john.smith
printEmailUser("I have no email") // unknown
}
fun strLen(s: String): Int = s.length
fun main(args: Array<String>) {
println(strLen("Hello")) // 5
// println(strLen(null)) // compile error: Null can not be a value of a non-null type String
}
// fun strLenSafe(s: String?): Int = s.length // compile error: Only safe (?.) or
// non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
fun strLenSafe(s: String?): Int = if(s != null) s.length else 0
fun main(args: Array<String>) {
println(strLenSafe("Hello")) // 5
println(strLenSafe(null)) // 0
}
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person(val name: String, val company: Company?)
fun Person.countryName() = this.company?.address?.country ?: "Unknown"
fun main(args: Array<String>) {
val michael = Person("Michael", Company("SCOOP",
Address("Eiler Straße 3P", 51107, "Köln", "Germany")))
val dilbert = Person("Dilbert", null)
println(michael.countryName()) // Germany
println(dilbert.countryName()) // Unknown
}
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person(val name: String, val company: Company?)
fun Person.countryName() = this.company!!.address!!.country // unsafe
fun main(args: Array<String>) {
val michael = Person("Michael", Company("SCOOP",
Address("Eiler Straße 3P", 51107, "Köln", "Germany")))
val dilbert = Person("Dilbert", null)
println(michael.countryName()) // Germany
println(dilbert.countryName()) // throws KotlinNullPointerException
}
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person(val name: String, val company: Company?)
fun Person?.hasEmployer() = (this != null) && (this.company != null)
fun checkEmployment(p: Person?) {
println(if(p.hasEmployer()) "employed" else "unemployed")
}
fun main(args: Array<String>) {
val michael = Person("Michael", Company("SCOOP",
Address("Eiler Straße 3P", 51107, "Köln", "Germany")))
val dilbert = Person("Dilbert", null)
checkEmployment(michael) // employed
checkEmployment(dilbert) // unemployed
checkEmployment(null) // unemployed
}
class Person(val firstName: String, val lastName: String) {
override fun equals(o: Any?): Boolean {
val otherPerson = o as? Person ?: return false
return otherPerson.firstName == firstName &&
otherPerson.lastName == lastName
}
override fun hashCode(): Int =
firstName.hashCode() * 37 + lastName.hashCode()
}
fun main(args: Array<String>) {
val p1 = Person("John", "Smith")
val p2 = Person("John", "Smith")
println(p1.equals(p2)) // true
println(p1.equals(42)) // false
}
// Java
public class User {
private final String id;
private String email;
public User(String id) { this.id = java.util.Objects.requireNonNull(id); }
public String getId() { return id; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
// Kotlin
fun main(args: Array<String>) {
val u = User("jsmith")
println(u.id?.toUpperCase()) // JSMITH
println(u.email?.toUpperCase()) // null
println(u.id.toUpperCase()) // JSMITH
println(u.email.toUpperCase()) // java.lang.IllegalStateException: u.email must not be null
}
// Java
public class User {
private final String id;
private String email;
public User(String id) { this.id = java.util.Objects.requireNonNull(id); }
public String getId() { return id; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
// Kotlin
class UpperUser(id: String): User(id) {
override fun getId(): String = super.getId().toUpperCase()
override fun getEmail(): String? = super.getEmail()?.toUpperCase()
override fun setEmail(email: String?) {
super.setEmail(email?.toUpperCase())
}
}
val i: Int = 1
val list: List<Int> = listOf(1, 2, 3)
Kotlin types that correspond to Java primitive types:
Byte
, Short
, Int
, Long
Float
, Double
Char
Boolean
Kotlin | Java | Comments | Code |
---|---|---|---|
Any |
Object |
the supertype of all non-nullable types in Kotlin |
|
Unit |
Void |
can be also used as a type argument |
|
Nothing |
- |
used as return type for functions that never complete successfully |
|
import java.sql.Connection
import java.sql.ResultSet
interface QueryExecutor {
fun execute(sql: String): ResultSet
}
class QueryExecutorImpl(val connectionProvider: () -> Connection): QueryExecutor {
override fun execute(sql: String): ResultSet =
connectionProvider().createStatement().executeQuery(sql)
}
// Change request: the returned ResultSet should mask the values from password-containing columns
import java.sql.Connection
import java.sql.ResultSet
interface QueryExecutor {
fun execute(sql: String): ResultSet
}
class QueryExecutorImpl(val connectionProvider: () -> Connection): QueryExecutor {
override fun execute(sql: String): ResultSet =
SafeResultSet(connectionProvider().createStatement().executeQuery(sql))
}
class SafeResultSet(val rs: ResultSet) : ResultSet by rs {
override fun getString(columnLabel: String?): String =
if(columnLabel!!.toLowerCase().contains("password")) "***"
else rs.getString(columnLabel)
override fun getString(columnIndex: Int): String =
getString(rs.metaData.getColumnLabel(columnIndex))
}
object CountryTable : IntIdTable() {
val name = varchar("name", 250).uniqueIndex()
val iso = varchar("iso", 2).uniqueIndex()
}
class Country(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Country>(CountryTable)
var name: String by CountryTable.name
var iso: String by CountryTable.iso
}
fun main(args: Array<String>) {
Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
transaction {
logger.addLogger(StdOutSqlLogger)
create (CountryTable)
Country.new { name = "Germany"; iso = "de" }
Country.new { name = "Russia"; iso = "ru" }
Country.new { name = "United States"; iso = "us" }
val russia = Country.find { CountryTable.iso.eq("ru") }.first()
println(russia.name) // Russia
}
}
class Email {/* ... */}
fun loadEmails(person: Person): List<Email> {
println("Load emails for ${person.name}")
return listOf(/*...*/)
}
class Person(val name: String /* ... */) {
val emails: List<Email> = loadEmails(this)
}
// emails will be loaded even if they are not required
class Email {/* ... */}
fun loadEmails(person: Person): List<Email> {
println("Load emails for ${person.name}")
return listOf(/*...*/)
}
class Person(val name: String /* ... */) {
private var nullableEmails: List<Email>? = null
val emails: List<Email>
get() {
if (nullableEmails == null) {
nullableEmails = loadEmails(this)
}
return nullableEmails!!
}
}
// not thread-safe
class Email {/* ... */}
fun loadEmails(person: Person): List<Email> {
println("Load emails for ${person.name}")
return listOf(/*...*/)
}
class Person(val name: String /* ... */) {
val emails by lazy { loadEmails(this) }
}
// lazy is a special case of property delegation
fun readNumbers(reader: BufferedReader): List<Int?> {
val result = ArrayList<Int?>()
for (line in reader.lineSequence()) {
result.add(line.toIntOrNull())
}
return result
}
fun addValidNumbers(numbers: List<Int?>) {
val validNumbers = numbers.filterNotNull()
println("Sum of valid numbers: ${validNumbers.sum()}")
println("Invalid numbers: ${numbers.size - validNumbers.size}")
}
|
|
|
|
|
|
|
Collection-creation functions
Collection type | Read-only | Mutable |
---|---|---|
List |
listOf |
mutableListOf, arrayListOf |
Set |
setOf |
mutableSetOf, hashSetOf, linkedSetOf, sortedSetOf |
Map |
mapOf |
mutableMapOf, hashMapOf, linkedMapOf, sortedMapOf |
fun main(args: Array<String>) {
val mlist = mutableListOf(3, 6, 7)
mlist[0] = 1
println(mlist) // [1, 6, 7]
val list = listOf(3, 6, 7)
// list[0] = 1 // doesn't compile
println(list.filter { it % 2 == 1 }) // [3, 7]
val map = mutableMapOf(1 to "Alice", 2 to "Bob")
println(map[1]?.toUpperCase()) // ALICE
}
val numbers = arrayOf(1, 2, 3, 4, 5)
println(numbers.toList()) // [1, 2, 3, 4, 5]
val nulls = arrayOfNulls<Int>(4)
println(nulls.joinToString()) // null, null, null, null
val bytes = byteArrayOf(83, 99, 111, 111, 112)
println(String(bytes)) // Scoop
val squares = IntArray(5) { i -> (i+1) * (i+1) }
println(squares.joinToString()) // 1, 4, 9, 16, 25
val letters = Array<String>(26) { i -> ('a' + i).toString() }
println(letters.joinToString("")) // abcdefghijklmnopqrstuvwxyz
val names = arrayOf("Alice", "Bob", "Carol")
names.forEachIndexed { index, name -> // 1: Alice
println("${index+1}: $name") // 2: Bob
} // 3: Carol
fun max(vararg numbers: Int) =
numbers.reduce{max, e -> if(max > e) max else e}
...
...
...
println(max(5, 3, 7, 2)) // 7
val values = intArrayOf(6, 9, 1, 8)
println(max(5, 3, *values, 7, 2)) // 9
val strings = listOf("a", "b", "c")
println("%s/%s/%s".format(*strings.toTypedArray())) // a/b/c
class Person(val name: String, var spouse: Person? = null) {
fun marry(p: Person) {
spouse = p
p.spouse = this
println("$name is now married to ${p.name}")
}
}
fun main(args: Array<String>) {
val john = Person("John")
val anna = Person("Anna")
john.marry(anna) // John is now married to Anna
}
class Person(val name: String, var spouse: Person? = null) {
infix fun marry(p: Person) {
spouse = p
p.spouse = this
println("$name is now married to ${p.name}")
}
}
fun main(args: Array<String>) {
val john = Person("John")
val anna = Person("Anna")
john marry anna // John is now married to Anna
}
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
println(s1 union s2) // [aaa, bbb, ccc]
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
println(s1 union s2) // [aaa, bbb, ccc]
for(i in 1 until 10 step 2) print("$i ") // 1 3 5 7 9
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
println(s1 union s2) // [aaa, bbb, ccc]
for(i in 1 until 10 step 2) print("$i ") // 1 3 5 7 9
for(i in 1.until(10).step(2)) print("$i ") // 1 3 5 7 9
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
println(s1 union s2) // [aaa, bbb, ccc]
for(i in 1 until 10 step 2) print("$i ") // 1 3 5 7 9
for(i in 1.until(10).step(2)) print("$i ") // 1 3 5 7 9
println(mapOf(1 to "Alice", 2 to "Bob")) // {1=Alice, 2=Bob}
val s1 = setOf("aaa", "bbb")
val s2 = setOf("bbb", "ccc")
println(s1.union(s2)) // [aaa, bbb, ccc]
println(s1 union s2) // [aaa, bbb, ccc]
for(i in 1 until 10 step 2) print("$i ") // 1 3 5 7 9
for(i in 1.until(10).step(2)) print("$i ") // 1 3 5 7 9
println(mapOf(1 to "Alice", 2 to "Bob")) // {1=Alice, 2=Bob}
println(mapOf(1.to("Alice"), 2.to("Bob"))) // {1=Alice, 2=Bob}
import kotlin.math.*
data class Point(val x: Double, val y: Double)
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
fun main(args: Array<String>) {
val p1 = fromPolar(4.0, PI / 5)
val p2 = fromPolar(3.0, 7 * PI / 10)
val dx = p2.x - p1.x
val dy = p2.y - p1.y
val d = sqrt(dx * dx + dy * dy)
println("distance = $d") // distance = 5.0
}
import kotlin.math.*
data class Point(val x: Double, val y: Double)
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
fun main(args: Array<String>) {
val (x1, y1) = fromPolar(4.0, PI / 5)
val (x2, y2) = fromPolar(3.0, 7 * PI / 10)
val dx = x2 - x1
val dy = y2 - y1
val d = sqrt(dx * dx + dy * dy)
println("distance = $d") // distance = 5.0
}
Data classes can be destructured directly.
import kotlin.math.*
class Point(val x: Double, val y: Double) {
operator fun component1() = x
operator fun component2() = y
}
fun fromPolar(r: Double, phi: Double) = Point(r * cos(phi), r * sin(phi))
fun main(args: Array<String>) {
val (x1, y1) = fromPolar(4.0, PI / 5)
val (x2, y2) = fromPolar(3.0, 7 * PI / 10)
val dx = x2 - x1
val dy = y2 - y1
val d = sqrt(dx * dx + dy * dy)
println("distance = $d") // distance = 5.0
}
Non-data classes must implement componentN()
.
fun printEntries(map: Map<String, String>) {
for ((key, value) in map) {
println("$key -> $value")
}
}
fun main(args: Array<String>) {
printEntries(mapOf(
"040" to "Hamburg",
"0221" to "Köln",
"0611" to "Wiesbaden"
))
}
040 -> Hamburg
0221 -> Köln
0611 -> Wiesbaden
fun printWithIndex(list: List<*>) {
for ((index, element) in list.withIndex()) {
println("$index: $element")
}
}
fun main(args: Array<String>) {
printWithIndex(listOf("Zero", "One", "Two"))
}
0: Zero
1: One
2: Two
fun splitFilename(fullName: String) {
val (name, extension) = fullName.split('.', limit = 2)
println("name: $name, extension: $extension")
}
fun main(args: Array<String>) {
splitFilename("scoop.kt")
}
name: scoop, extension: kt
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<T: Animal>(val list: List<T>) {
fun get(name: String): T = list.first{it.name == name}
}
fun feedByName(name: String, herd: Herd<Animal>) {
herd.get(name).feed()
}
fun main(args: Array<String>) {
val animals = Herd(listOf(Animal("Unicorn"), Animal("Godzilla")))
val cats = Herd(listOf(Cat("Nala"), Cat("Sammy", "orange")))
feedByName("Godzilla", animals)
// feedByName("Sammy", cats) // compile error: Type mismatch
}
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<T: Animal>(val list: List<T>) {
fun get(name: String): T = list.first{it.name == name}
}
fun feedByName(name: String, herd: Herd<out Animal>) { // use-site variance
herd.get(name).feed() // Java: <? extends Animal>
}
fun main(args: Array<String>) {
val animals = Herd(listOf(Animal("Unicorn"), Animal("Godzilla")))
val cats = Herd(listOf(Cat("Nala"), Cat("Sammy", "orange")))
feedByName("Godzilla", animals)
feedByName("Sammy", cats)
}
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<out T: Animal>(val list: List<T>) { // declaration-site variance
fun get(name: String): T = list.first{it.name == name}
}
fun feedByName(name: String, herd: Herd<Animal>) {
herd.get(name).feed()
}
fun main(args: Array<String>) {
val animals = Herd(listOf(Animal("Unicorn"), Animal("Godzilla")))
val cats = Herd(listOf(Cat("Nala"), Cat("Sammy", "orange")))
feedByName("Godzilla", animals)
feedByName("Sammy", cats)
}
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<T: Animal>(val list: MutableList<T>) {
fun accept(member: T) = list.add(member)
}
fun addGarfield(herd: Herd<Cat>) {
herd.accept(Cat("Garfield", "orange"))
}
fun main(args: Array<String>) {
val animals = Herd<Animal>(mutableListOf())
val cats = Herd<Cat>(mutableListOf())
// addGarfield(animals) // compile error: Type mismatch
addGarfield(cats)
}
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<T: Animal>(val list: MutableList<T>) {
fun accept(member: T) = list.add(member)
}
fun addGarfield(herd: Herd<in Cat>) { // use-site variance (Java: <? super Cat>)
herd.accept(Cat("Garfield", "orange"))
}
fun main(args: Array<String>) {
val animals = Herd<Animal>(mutableListOf())
val cats = Herd<Cat>(mutableListOf())
addGarfield(animals)
addGarfield(cats)
}
open class Animal(val name: String) {
fun feed() = println("$name is no longer hungry")
}
class Cat(name: String, var hairColor: String? = null): Animal(name)
class Herd<in T: Animal>(val list: MutableList<in T>) { // declaration-site variance
fun accept(member: T) = list.add(member)
}
fun addGarfield(herd: Herd<Cat>) {
herd.accept(Cat("Garfield", "orange"))
}
fun main(args: Array<String>) {
val animals = Herd<Animal>(mutableListOf())
val cats = Herd<Cat>(mutableListOf())
addGarfield(animals)
addGarfield(cats)
}
open class Animal(val name: String) { fun feed() = println("$name is no longer hungry") }
class Cat(name: String, var hairColor: String? = null): Animal(name)
interface Producer<out T> { fun get(name: String): T }
interface Consumer<in T> { fun accept(member: T) }
class Herd<T: Animal>(val list: MutableList<T>): Producer<T>, Consumer<T> {
override fun accept(member: T) { list.add(member) }
override fun get(name: String): T = list.first{it.name == name}
}
fun feedByName(name: String, herd: Producer<Animal>) {
herd.get(name).feed()
}
fun addGarfield(herd: Consumer<Cat>) {
herd.accept(Cat("Garfield", "orange"))
}
fun main(args: Array<String>) {
val animals = Herd<Animal>(mutableListOf(Animal("Unicorn"), Animal("Godzilla")))
val cats = Herd<Cat>(mutableListOf(Cat("Nala"), Cat("Sammy", "orange")))
feedByName("Godzilla", animals)
feedByName("Sammy", cats)
addGarfield(animals)
addGarfield(cats)
}
Type | Example | Variance | Restriction |
---|---|---|---|
Out-Projected |
Producer<out Cat> |
Covariance |
Type parameter cannot be used as a function argument or setter |
In-Projected |
Consumer<in Cat> |
Contravariance |
If the type parameter is returned from a function or getter, its type will be Any? (or the upper-bound if you specified a type parameter constraint) |
Star‑Projected |
MutableList<*> |
Every instance is a subtype |
Both of the above restrictions apply |
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2)) // 3
class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.joinToString(" ", transform = { p: Person -> p.name })) // Alice Bob
}
separator
transform
class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.joinToString(" ") { p: Person -> p.name }) // Alice Bob
}
class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.joinToString(" ") { p -> p.name }) // Alice Bob
}
class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.joinToString(" ") { it.name }) // Alice Bob
}
it
class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.joinToString(" ", transform = Person::name)) // Alice Bob
}
fun printProblemCounts(responses: Collection<String>) {
var clientErrors = 0
var serverErrors = 0
responses.forEach {
if (it.startsWith("4")) {
clientErrors++
} else if (it.startsWith("5")) {
serverErrors++
}
}
println("$clientErrors client errors, $serverErrors server errors")
}
fun main(args: Array<String>) {
val responses = listOf("200 OK", "418 I'm a teapot", "500 Internal Server Error")
printProblemCounts(responses) // 1 client errors, 1 server errors
}
data class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 31), Person("Bob", 29), Person("Carol", 31))
val ageGroups = people.groupBy { it.age }
println(ageGroups)
// {31=[Person(name=Alice, age=31), Person(name=Carol, age=31)], 29=[Person(name=Bob, age=29)]}
}
data class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 31), Person("Bob", 29), Person("Carol", 31))
val ageGroups = people.groupBy { it.age }
println(ageGroups)
// {31=[Person(name=Alice, age=31), Person(name=Carol, age=31)], 29=[Person(name=Bob, age=29)]}
println(ageGroups.mapValues{(_, list) -> list.map { it.name }})
// {31=[Alice, Carol], 29=[Bob]}
}
fun alphabet(): String {
val result = StringBuilder()
for (letter in 'A'..'Z') {
result.append(letter)
}
result.append("\nNow I know the alphabet!")
return result.toString()
}
fun main(args: Array<String>) {
println(alphabet())
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
// Now I know the alphabet!
}
fun alphabet(): String =
with(StringBuilder()) {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
toString()
}
fun main(args: Array<String>) {
println(alphabet())
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
// Now I know the alphabet!
}
with
is a standard library function that uses lambdas with receivers (see also: let
, run
, also
, apply
)Binary Operators | Unary operators | |||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main(args: Array<String>) {
var p1 = Point(10, 20)
val p2 = Point(30, 40)
println(p1 + p2) // Point(x=40, y=60)
p1 += p2
println(p1) // Point(x=40, y=60)
}
data class Point(val x: Int, val y: Int)
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
fun main(args: Array<String>) {
var p1 = Point(10, 20)
val p2 = Point(30, 40)
println(p1 + p2) // Point(x=40, y=60)
p1 += p2
println(p1) // Point(x=40, y=60)
}
class Person(val firstName: String, val lastName: String): Comparable<Person> {
override fun equals(other: Any?): Boolean {
if (other === this) return true
if (other !is Person) return false
return other.firstName == firstName && other.lastName == lastName
}
override fun compareTo(other: Person): Int =
compareValuesBy(this, other, Person::lastName, Person::firstName)
}
fun main(args: Array<String>) {
val p1 = Person("Alice", "Smith")
val p2 = Person("Bob", "Johnson")
println(p1 == p2) // false
println(p1 != p2) // true
println(p1 < p2) // false
println(p1 >= p2) // true
}
data class Point(val x: Int, val y: Int)
operator fun Point.get(index: Int) = when(index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
}
fun main(args: Array<String>) {
val p = Point(10, 20)
println(p[1]) // 20
}
data class Point(var x: Int, var y: Int)
operator fun Point.get(index: Int) = when(index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
}
operator fun Point.set(index: Int, value: Int) = when(index) {
0 -> x = value
1 -> y = value
else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
}
fun main(args: Array<String>) {
val p = Point(10, 20)
p[1] = 42
println(p[1]) // 42
}
in
operatordata class Point(val x: Int, val y: Int)
data class Rectangle(val upperLeft: Point, val lowerRight: Point)
operator fun Rectangle.contains(p: Point): Boolean {
return p.x in upperLeft.x until lowerRight.x &&
p.y in upperLeft.y until lowerRight.y
}
fun main(args: Array<String>) {
val rect = Rectangle(Point(10, 20), Point(50, 50))
println(Point(20, 30) in rect) // true
println(Point(5, 5) in rect) // false
}
rangeTo
and iterator
suspend fun requestImageUrls(query: String, count: Int = 20) = ...
suspend fun requestImageData(imageUrl: String) = ...
suspend fun createCollage(query: String, count: Int): BufferedImage {
val urls = requestImageUrls(query, count)
val images = mutableListOf<BufferedImage>()
for (index in 0 until urls.size) {
val image = requestImageData(urls[index])
images += image
}
val newImage = combineImages(images)
return newImage
}
suspend fun requestImageData(imageUrl: String) = suspendCoroutine<BufferedImage> { cont ->
JerseyClient.url(imageUrl)
.request(MediaType.APPLICATION_OCTET_STREAM)
.async()
.get(object : InvocationCallback<InputStream> {
override fun completed(response: InputStream) {
val image = ImageIO.read(response)
cont.resume(image)
}
override fun failed(throwable: Throwable) {
cont.resumeWithException(throwable)
}
})
}
annotations:
import com.github.jrubygradle.JRubyPrepare
import org.asciidoctor.gradle.AsciidoctorTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.gradle.api.Task
import org.gradle.jvm.tasks.Jar
import org.ysb33r.groovy.dsl.vfs.VFS
buildscript {
dependencies {
classpath("org.ysb33r.gradle:vfs-gradle-plugin:1.0")
classpath("commons-httpclient:commons-httpclient:3.1")
}
}
val version: String by project
val deckjsVersion: String by project
val asciidoctorBackendVersion: String by project
val downloadDir = File(buildDir,"download")
val templateDir = File(downloadDir,"templates")
val deckjsTmpDir = File(downloadDir,"deck.js.tmp")
val deckjsDir = File(downloadDir,"deck.js")
val deckjsVersionDir = File(downloadDir,"deck.js-master")
plugins {
application
kotlin("jvm") version "1.2.31"
id("org.jetbrains.dokka") version "0.9.16"
id("org.asciidoctor.convert") version "1.6.0"
id("com.github.jruby-gradle.base") version "1.6.0"
id("org.openjfx.javafxplugin") version "0.0.7"
}
repositories {
jcenter()
maven("https://dl.bintray.com/kotlin/exposed")
}
apply {
plugin("application")
plugin("org.jetbrains.dokka")
plugin("com.github.jruby-gradle.base")
plugin("org.ysb33r.vfs")
plugin("org.asciidoctor.convert")
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
group = "de.scoopsoftware"
javafx {
modules = listOf("javafx.controls", "javafx.fxml")
}
dependencies {
compile(kotlin("reflect"))
compile(kotlin("stdlib"))
compile("org.slf4j:slf4j-simple:1.7.25")
compile("org.jetbrains.exposed:exposed:0.10.1")
compile("com.h2database:h2:1.4.197")
compile("io.javalin:javalin:1.4.1")
compile("no.tornado:tornadofx:1.7.15")
testCompile("io.kotlintest:kotlintest:2.0.7")
gems("rubygems:haml:5.0.4")
jrubyExec("org.jruby:jruby-complete:9.2.6.0")
}
tasks {
register("download") {
doLast {
mkdir(downloadDir!!)
with(VFS()) {
cp(mapOf("recursive" to true, "overwrite" to true, "smash" to true),
"zip:https://github.com/asciidoctor/asciidoctor-deck.js/archive/${asciidoctorBackendVersion}.zip!asciidoctor-deck.js-${asciidoctorBackendVersion}/templates",
templateDir)
cp(mapOf("recursive" to true, "overwrite" to true, "smash" to true),
"zip:https://github.com/imakewebthings/deck.js/archive/${deckjsVersion}.zip!deck.js-${deckjsVersion}",
deckjsDir)
}
deckjsVersionDir.renameTo(deckjsDir)
}
}
}
val downloadTask = tasks.get("download")
val jrubyPrepare = tasks.get("jrubyPrepare")
with(downloadTask) {
description = "Download extra deckjs resources"
outputs.dirs(templateDir, deckjsDir)
}
tasks.withType<Jar> {
manifest {
attributes(mapOf(
"Implementation-Title" to "kotlinTalk",
"Main-Class" to "de.scoopsoftware.kotlintalk.HelloWeb",
"Implementation-Version" to version
))
}
}
tasks.withType<AsciidoctorTask> {
dependsOn(jrubyPrepare, downloadTask)
sources(delegateClosureOf<PatternSet> {
include("kotlin-slides.adoc")
})
resources(delegateClosureOf<CopySpec> {
from (sourceDir) { include("images/**", "deck.js/**") }
from (downloadDir) { include("deck.js/**") }
})
backends("html5")
attributes.putAll(mapOf(
"prjdir" to "${project.rootDir}",
"sourcedir" to "${project.rootDir}/src/main",
"imagesdir" to "./images"
))
options(mapOf("template_dirs" to listOf(File(templateDir,"haml").absolutePath)))
}
application {
mainClassName = "de.scoopsoftware.kotlintalk.HelloWeb"
}
tasks.register("bar") {
group = "sample"
doLast { println("Bar!") }
}