NULLABLE TYPES
fun function(x: Int = 0, y: Int = 5, z: Int = 0) = 2 * x + y - z
fun functionTest() {
val a = function(1, null, 3) // Doesn't compile!!!
}
NULLABLE TYPES
fun function(x: Int = 0, y: Int? = 5, z: Int = 0) = 2 * x + y - z
// Doesn't compile!!!
fun functionTest() {
val a = function(1, null, 3)
}
NULLABLE TYPES
fun function(x: Int = 0, y: Int? = 5, z: Int = 0): Int {
if (y != null) {
return 2 * x + y - z
} else {
return 0
}
}
fun functionTest() {
val a = function(1, null, 3)
}
NULLABLE TYPES
fun function(x: Int = 0, y: Int? = 5, z: Int = 0): Int {
return if (y != null) 2 * x + y - z else 0
}
fun functionTest() {
val a = function(1, null, 3)
}
NULLABLE TYPES
class Driver(val name: String, val surname: String)
class Car(val driver: Driver?)
class Garage(var car: Car?)
fun garageTest() {
var driver = Driver("John","Doe")
var car = Car(driver)
var garage = Garage(car)
//println(garage.car.driver.name) // Doesn't compile
println(garage.car?.driver?.name) // May print null
println(garage.car?.driver?.name?:"Unknown") // or Unknown
}
CLASSES
- PUBLIC BY DEFAULT
- NO PACKAGE SCOPE
- FINAL BY DEFAULT
- FILENAME DOESN'T MATTER
- MANY PUBLIC CLASSES IN ONE FILE
- GETTERS BY DEFAULT (ON JAVA SIDE)
SIMPLEST CLASS
class EmptyClass
SAMPLE CLASS
class Driver {
val name: String
val surname: String
var age: Int
var eyeColor: String
var married = false
var birthDate: Date? = null
constructor(name: String,
surname: String,
age: Int,
eyeColor: String = "black") {
this.name = name
this.surname = surname
this.age = age
this.eyeColor = eyeColor
}
fun sayHello() = "Hello. I'm $name $surname. I'm $age years old."
}
PRIMARY CONSTRUCTOR
class Driver(
val name: String,
val surname:String,
var age: Int,
var eyeColor:String = "black"
){
var married = false
var birthDate: Date? = null
fun sayHello() = "Hello. I'm $name $surname. I'm $age years old."
}
GETERS AND SETERS
class Basket {
var apples = 0
var oranges = 0
var fruits: Int
get() {
return apples + oranges
}
set(value) {
apples = value
oranges = value
}
}
fun main(args: Array<String>) {
val basket = Basket()
basket.apples = 1
basket.oranges = 2
println(basket.fruits) // 3
basket.fruits = 5
println(basket.fruits) //10
}
CLASS USAGE
class Driver(
val name: String,
val surname:String,
var age: Int,
var eyeColor:String = "black"
){
var married = false
var birthDate: Date? = null
fun sayHello() = "Hello. I'm $name $surname. I'm $age years old."
}
fun main(args: Array<String>) {
val driver1 = Driver("John", "Doe", 27)
val driver2 = Driver("John", "Doe", eyeColor = "green", age = 30)
val driver3 = Driver("John", "Doe", 30, "green")
println(driver2 == driver3) //Equals false
}
EQUALS
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Driver
if (name != other.name) return false
if (surname != other.surname) return false
if (age != other.age) return false
if (eyeColor != other.eyeColor) return false
if (married != other.married) return false
if (birthDate != other.birthDate) return false
return true
}
HASHCODE
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + surname.hashCode()
result = 31 * result + age
result = 31 * result + eyeColor.hashCode()
result = 31 * result + married.hashCode()
result = 31 * result + (birthDate?.hashCode() ?: 0)
return result
}
DATA CLASS
data class Driver(
val name: String,
val surname: String,
var age: Int,
var eyeColor: String = "black"
) {
var married = false
var birthDate: Date? = null
fun sayHello() = "Hello. I'm $name $surname. I'm $age years old."
}
fun main(args: Array<String>) {
val driver1 = Driver("John", "Doe", 27)
val driver2 = Driver("John", "Doe", eyeColor = "green", age = 30)
val driver3 = Driver("John", "Doe", 30, "green")
println(driver2 == driver3) //Equals true!
}
DATA CLASS
data class Point(val x: Double, val y: Double)
fun main(args: Array<String>) {
val p1 = Point(1.0, 2.0)
val p2 = p1.copy(y = 3.0)
val (x, y) = p2
assert(x == 1.0)
assert(y == 3.0)
}
DATA CLASS
- PROVIDE EQUALS
- PROVIDE HASHCODE
- PROVIDE TOSTRING
- PROVIDE COPY METHOD
- ALLOW DESTRUCTURING
- cannot be abstract, open, sealed or inner
- (before 1.1) may only implement interfaces
INHERITANCE
interface Person {
fun sayHello(): String
}
abstract class PersonWithAge(var age: Int) : Person
open class Driver(
age: Int,
val name: String,
val surname: String,
var eyeColor: String = "black"
) : PersonWithAge(age), Serializable {
var married = false
var birthDate: Date? = null
override fun sayHello() = "Hello. I'm $name $surname. I'm $age years old."
}
OBJECT
object Version {
val major = 1
val minor = 7
val build = 123
fun printVersion() {
println("v$major.$minor.$build")
}
}
fun main(args: Array<String>) {
Version.printVersion()
}
COMPANION OBJECT
class SimpleClass {
fun simpleMethod() {
print(major)
printVersion()
}
companion object {
val major = 1
val minor = 7
val build = 123
fun printVersion() {
println("v$major.$minor.$build")
}
}
}
SEALED CLASS
sealed class Message(val text: String) {
class SuccessMessage(text: String) : Message(text)
class WarningMessage(text: String, val details: String) : Message(text)
class ErrorMessage(text: String, val errorCode: Int) : Message(text)
}
fun main(args: Array<String>) {
val message = Message.ErrorMessage("Not Found", 404)
}
MUTABILITY
val immutableList = listOf(1, 2, 3)
val immutableSet = setOf(1, 2, 3)
val immutableMap = mapOf(Pair("a", 1), Pair("b", 1))
val list = mutableListOf(1, 2, 3)
val set = mutableSetOf(1, 2, 3)
val map = mutableMapOf(Pair("a", 1), Pair("b", 1))
CLASSIC COLLECTIONS
val list = arrayListOf(1, 2, 3)
val set = hashSetOf(1, 2, 3)
val map = hashMapOf(Pair("a", 1), Pair("b", 1))
ACCESS COLLECTIONS
val list = mutableListOf(1, 2, 3, 4, 5)
list.set(0, 7)
list[0] = 7
JAVA 8 STREAMS
fun javaStream(employes: ArrayList<Employee>) {
employes.stream()
.filter({ e -> e!=null })
.filter({ e -> e.salary>BigDecimal(3000)})
.filter({ it.salary> BigDecimal(5000) })
.sorted({ o1, o2 -> o2.salary.compareTo(o1.salary)})
.map({"${it.name} gets ${it.salary} USD"})
.forEach({ println(it)})
}
KOTLIN "STREAMS"
fun kotlinStream(employes: ArrayList<Employee>) {
employes
.filterNotNull()
.filter { employee -> employee.salary > BigDecimal(3000) }
.filter { it.salary > BigDecimal(5000) }
.sortedByDescending { it.salary }
.map { "${it.name} gets ${it.salary} USD" }
.forEach { println(it) }
}
GROUPING
fun kotlinGroup(employes: ArrayList<Employee>) {
val map: Map<BigDecimal, List<Employee>>= employes.groupBy { it.salary }
}
EXTENSIONS
fun LocalDateTime.toDate(): Date {
return Date
.from(this.atZone(ZoneId.systemDefault()).toInstant())
}
fun Date.toLocalDateTime(): LocalDateTime {
return LocalDateTime
.ofInstant(this.toInstant(), ZoneId.systemDefault())
}
fun main(args: Array<String>) {
val date = Date()
val localDateTime = date.toLocalDateTime()
print(date == localDateTime.toDate()) // true
}
EXTENSIONS
fun String.toPokemonCase(): String {
val sb = StringBuilder()
for (i in 0..this.length - 1) {
when {
(i % 2 == 0) -> sb.append(this[i].toUpperCase())
else -> sb.append(this[i].toLowerCase())
}
}
return sb.toString()
}
fun main(args: Array<String>) {
println("hello kotlin".toPokemonCase()) // HeLlO KoTlIn
}
CONDITIONAL EXPRESSIONS
fun max(a: Int, b: Int) = if (a > b) a else b
Smart Cast
interface Creature {
fun name(): String
}
class Cat : Creature {
override fun name() = "Cat"
fun meow() = print("Meow")
}
class Bird : Creature {
override fun name() = "Bird"
fun fly() = print("I'm flying")
}
fun smartCast(creature: Creature) {
print(creature.name())
if (creature is Cat) {
creature.meow()
} else if (creature is Bird) {
creature.fly()
}
}
For Loop
fun collectionLoop(list: List<String>) {
for (s in list) {
print(s.toUpperCase())
}
}
Range loop
fun rangeLoop() {
for (i in 1..19) println(i)
for (i in 1..19 step 3) println(i)
}
Range testing
fun rangeTest() {
val a = 3.14
if (a in 3.0..3.2) print("between 3.0 and 3.2")
}
IN OPERATOR
fun inCollectionTest(){
val list = arrayListOf("aaa","bbb","ccc")
if ("aaa" in list)
println("Yes: list contains aaa")
if ("ddd" in list)
println("Yes: list contains ddd")
else
println("No: list doesn't contains ddd")
}
WHEN EXPRESSION
fun main(args: Array<String>) {
cases("Hello")
cases(1)
cases(System.currentTimeMillis())
cases(BigDecimal.ZERO)
cases("hello")
}
fun cases(obj: Any) {
when (obj) {
1 -> println("One")
"Hello" -> println("Greeting")
is Long -> println("Long")
!is String -> println("Not a string")
else -> println("Unknown")
}
}
WITH EXPRESSION
data class Person(var name:String,var surname:String, var age: Int){
fun sayHello() = println("Hi, I'm $name $surname")
}
fun main(args: Array<String>) {
val person = Person("john","doe",42)
with(person){
sayHello()
name = name.capitalize()
surname = surname.capitalize()
age = if (age < 0) 0 else age
sayHello()
}
}
LET EXPRESSION
fun letExpression(person: Person?){
person?.let {
it.sayHello()
}
}
APPLY EXPRESSION
class Point3D {
var x=0; var y=0; var z=0;
override fun toString() = "[$x,$y,$z]"
}
fun applyExpression() {
val p1 = Point3D().apply { x=2; y=4; z=3 }
val p2 = Point3D().apply { x=2; y=4; z=3 }
}
IT'S JUST KOTLIN
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
MAP DESTRUCTURING
fun mapDestructuring() {
val map = hashMapOf<String, Int>()
map.put("one", 1)
map.put("two", 2)
for ((key, value) in map) {
println("key = $key, value = $value")
}
}
ONELINE MAP CREATION
fun mapOf() {
val map = mapOf("one" to 1, "two" to 2)
for ((key, value) in map) {
println("key = $key, value = $value")
}
}
ENHANCED DEPRECATED AND TODO
class TestClass {
@Deprecated("This function is deprecated",
ReplaceWith("newMethod()"),
DeprecationLevel.ERROR)
fun oldMethod() {
TODO("OLD METHOD")
}
fun newMethod() {
TODO("NEW METHOD")
}
}
LAZY VERIABLES
class Calculator {
val sumFrom1To100: Int by lazy {
println("counting...")
val sum = (1..100).sum()
sum
}
}
fun main(args: Array<String>) {
val calc = Calculator()
println("sum is ${calc.sumFrom1To100}")
println("sum is ${calc.sumFrom1To100}")
println("sum is ${calc.sumFrom1To100}")
}
INFIX FUNCTIONS
data class Point(val x: Int, val y: Int) {
infix fun plus(p2: Point): Point {
return Point(this.x + p2.x, this.y + p2.y)
}
infix fun minus(p2: Point): Point {
return Point(this.x - p2.x, this.y - p2.y)
}
}
fun main(args: Array<String>) {
val p1 = Point(2, 5)
val p2 = Point(7, 2)
println(p1) // Point(x=2, y=5)
println(p2) // Point(x=7, y=2)
println(p1 plus p2) // Point(x=9, y=7)
println(p1 minus p2) // Point(x=-5, y=3)
}
OPERATORS
data class Point(val x: Int, val y: Int) {
operator fun plus(p2: Point): Point {
return Point(this.x + p2.x, this.y + p2.y)
}
operator fun minus(p2: Point): Point {
return Point(this.x - p2.x, this.y - p2.y)
}
}
fun main(args: Array<String>) {
val p1 = Point(2, 5)
val p2 = Point(7, 2)
println(p1) // Point(x=2, y=5)
println(p2) // Point(x=7, y=2)
println(p1 + p2) // Point(x=9, y=7)
println(p1 - p2) // Point(x=-5, y=3)
}
MULTILINE STRING
val html = """<html>
<head></head>
<body style="background-color: black">
<h1>Hello Kotlin</h1>
<h2>Path is c:\x\y\z.txt</h2>
<pre>
SELECT column_name(s)
FROM table1
INNER JOIN table2 ON table1.column_name = table2.column_name;
</pre>
</body>
</html>
"""
LAMBDAS
fun doTheMagic(a: Int, b: Int, function: (Int, Int) -> Int): Int {
print("apply oparation on $a and $b")
return function(a, b)
}
fun lambdaTest() {
val five = doTheMagic(3, 2, { x, y -> x + y })
val ten = doTheMagic(5, 2, { x, y -> x * y })
val division = { x: Int, y: Int -> x / y }
val two = doTheMagic(10, 2, division)
val alsoTwo = division(10, 2)
}
LAMBDAS
fun doTheMagic(a: Int, b: Int, function: (Int, Int) -> Int): Int {
print("apply oparation on $a and $b")
return function(a, b)
}
fun lambdaTest() {
val five = doTheMagic(3, 2) {
x, y -> x + y
}
val ten = doTheMagic(5, 2) {
x, y -> x * y
}
val division = { x: Int, y: Int -> x / y }
val two = doTheMagic(10, 2, division)
val alsoTwo = division(10, 2)
}
LAMBDAS BYTECODE
public final static lambdaTest()V
L0
LINENUMBER 8 L0
ICONST_3
ICONST_2
GETSTATIC pl/jug/ex010/LambdasKt$lambdaTest$five$1.INSTANCE : Lpl/jug/ex010/LambdasKt$lambdaTest$five$1;
CHECKCAST kotlin/jvm/functions/Function2
INVOKESTATIC pl/jug/ex010/LambdasKt.doTheMagic (IILkotlin/jvm/functions/Function2;)I
ISTORE 0
L1
LINENUMBER 11 L1
RETURN
L2
LOCALVARIABLE five I L1 L2 0
MAXSTACK = 3
MAXLOCALS = 1
LAMBDAS BYTECODE
public final static doTheMagic(IILkotlin/jvm/functions/Function2;)I
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 2
L0
LINENUMBER 4 L0
ALOAD 2
ILOAD 0
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ILOAD 1
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
INVOKEINTERFACE kotlin/jvm/functions/Function2.invoke (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
IRETURN
L1
LOCALVARIABLE a I L0 L1 0
LOCALVARIABLE b I L0 L1 1
LOCALVARIABLE function Lkotlin/jvm/functions/Function2; L0 L1 2
MAXSTACK = 3
MAXLOCALS = 3
INLINE FUNCTION
inline fun doTheMagic(a: Int, b: Int, function: (Int, Int) -> Int): Int {
return function(a, b)
}
fun lambdaTest() {
val five = doTheMagic(3, 2) {
x, y -> x + y
}
}
INLINE BYTECODE
public final lambdaTest()V
L0
LINENUMBER 9 L0
ALOAD 0
ASTORE 2
ICONST_3
ISTORE 3
ICONST_2
ISTORE 4
NOP
L1
LINENUMBER 15 L1
ILOAD 3
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ILOAD 4
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
ISTORE 5
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
ISTORE 6
L2
LINENUMBER 11 L2
ILOAD 6
ILOAD 5
IADD
L3
GOTO L4
L4
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
GOTO L5
L5
L6
LINENUMBER 9 L6
ISTORE 1
L7
LINENUMBER 13 L7
RETURN
L8
LOCALVARIABLE x I L2 L4 6
LOCALVARIABLE y I L2 L4 5
LOCALVARIABLE $i$a$1$doTheMagic I L2 L4 7
LOCALVARIABLE this_$iv Lpl/jug/ex010/Lambdas; L1 L5 2
LOCALVARIABLE a$iv I L1 L5 3
LOCALVARIABLE b$iv I L1 L5 4
LOCALVARIABLE $i$f$doTheMagic I L1 L5 8
LOCALVARIABLE five I L7 L8 1
LOCALVARIABLE this Lpl/jug/ex010/Lambdas; L0 L8 0
MAXSTACK = 2
MAXLOCALS = 9