1.1 Executing Kotlin program without local compiler.
- You can execute Kotlin program in https://play.kotlinlang.org/
- It’s IDE based on Web Platform.
1.2 Installing Kotlin compiler in your local computer.
- Itellij > New Project > Java > Kotlin/JVM > Src > New File > …
1.3 Compiling Kotlin file at Command line
1 | > kotlinc-jvm XXX.kt // creating class file for Jvm. |
2.1 Using nullable type in Kotlin
Unlike Java, Kotlin basically remove the possibility of null. so you should define the type differently to use nullable type.
1 | fun main() { |
somtimes someone doesn’t have a middle name. so defining the middle name as nullable type is proper. you can define nullable type as adding question mark(= ?).
1 | fun main() { |
prior to access middle name, if you checked nullable type whether it is null or not, then this type is automatically casted to non null type. so you don’t need to use double exclamation mark. but actually double exclamation mark (= !!) is code smell. This keyword would be one of the situation can occur NullPointerException. so Let’s use safe call operator (= ?.) instead of double exclamation mark.
1 | fun main() { |
You can keep the safety as using the safe call operator and you always process visibly the null with Elvis operator.
2.3 Method overloading for Java
Java doesn’t support default value in parameters. so It’s doesn’t support also the constructor doesn’t have the value for optional parameters.
1 | fun main() { |
but if you define @JvmOverloads annotation on the addProduct() funcion, This function will compile many constructors to support method default parameter in Java with overloading technique. the below is the result when you decompile the addProduct() function that is included @JvmOverloads annotation.
1 |
|
You should remember the important one that @JvmOverloads annotation doesn’t call super keyword. so it means @JvmOverloads annotation only call all arguments constructor for super class.
2.4 Casting type explicitly.
Kotlin doesn’t support cast the data type to more wide implicitly. it means that integer data type can’t assign to long type. but the kotlin offers the useful function like toInt(), toLong() to cast between them.
1 | fun main() { |
2.6 Power
Many languages basically support power operator (= ^). but unlike them, the kotlin doens’t support. but you can use Math.pow(double a, double b) function if you need it. additionally you can also use Double.pow(x: Double) or Float.pow(x: Float) functions.
you should remember the important one that unlike double, float type, integer type doesn’t support pow() function. so you should make it yourself like the below if you want.
1 | fun Int.pow(x: Int) = toDouble().pow(x).toInt() |
infix keyword can make new custom operator that operate as you want. let’s try to make power operator with infix keyword.
1 | fun main() { |
2.9 Creating Pair instance with to keyword
Pair is the data class has two elements named first, second. It is same concept with Map type in Java. the kotlin language support to keyword to create Pair instance easily.
1 | public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that) |
The below code show the usage of to keyword. the results of p1, p2 is same. (p1 = p2)
1 | fun main() { |
You can create the Pair instance easily with mapOf() and to keyword.
1 | fun main() { |
You can separate key and value like the below code if you want. It’s so intutive.
1 | fun main() { |
3.1 Understaning the difference between const val and val
all of them support to define the immutable data. but
const keyword support to initialize the data when the source code are compiled (compile time initialization). unlike this, val keyword initialize the data when the service is launched (run time initialization).
You should know the role const and val is different. const do access control one like private, inline. but val is a keyword to define variables.
1 | class Task(val name: String, _priority: Int) { |
MINPRIORITY, MAX_PRIORITY, DEFAULT_PRIORITY values will initialize when this source code is compiled because of _const.
3.2 Creating getter, setter
자바에서 전통적으로 객체 내에 존재하는 필드들을 접근할 때에는 getter, setter 함수를 만들어서 사용했다. getter, setter 를 사용하는 이유는 객체지향 프로그래밍 관점에서 encapsulation, 정보은닉 등의 이유로 사용된 것인데 코틀린에서는 Java 코드로 변환할 때 이를 자체적으로 만들어주기 때문에 따로 getter / setter 함수를 정의하지 않고 직접 필드에 접근해도 상관 없다.
1 | fun main() { |
getter / setter 에서 로그를 추가한다거나, 데이터 검증, 전처리 작업, 후처리 작업 등도 진행할 수 있다. 이런 경우를 위해서 코틀린에서는 getter, setter 함수를 커스터마이징 할 수 있는 기능을 제공한다. get(), set() 함수의 형태로 제공한다.
1 | fun main() { |
get(), set() 함수에서 field 값을 사용하는 것을 볼 수 있는데, 이는 priority 값을 의미한다. 저 곳에 priority 값을 바로 넣어주면 재귀가 발생해서 결국 stackoverflowexception 이 발생한다. 이를 개선하기 위해서 코틀린은 accessor 영역에서 자기 값을 얻기 위해 field 라는 값을 제공한다. 이를 backing field 라고 부른다.
3.3 데이터 클래스 정의하기
자바의 enum 처럼 데이터의 목적으로 사용하고 싶은 클래스를 만들어야 할 경우 클래스를 정의할 때 data 키워드를 사용하면 된다.
1 | fun main() { |
products.size 값은 1이 나올 것이다. data 클래스의 경우 동일한 값을 가지고 있는 객체끼리는 같은 객체로 본다. 그렇기 때문에 setOf() 함수를 통해 두 객체 p1, p2 를 넣을 때 같은 객체로 보고 중복되어 하나는 제거 된다.
객체의 동일성에 대한 이야기가 나온 김에 추가적으로 copy() 함수에 대한 이야기를 해보자. 이 함수는 깊은 복사가 아닌 얕은 복사를 수행한다. 그렇기 때문에 복사본 객체와 원본 객체는 서로 다른 객체이다.
1 | fun main() { |
복사된 item2 객체와 원본 item1 객체가 서로 다른 객체임을 item1 === item2 연산을 통해 확인할 수 있다. 하나 더 중요한 부분은 그 안에서 공유하고 있는 Product 객체는 서로 같은 객체를 바라보고 있음을 item1.product === item2.product 에서 확인이 가능하다.
3.4 Backing property technique
when you want to control reading the properties, initializing the properties in the class, you can use getter function.
1 | fun main() { |
in apply() function, messages’s getter method is called. and this coding style is for lazy initialization. the kotlin offers lazy delegation function so we can implement this code more easily.
1 | fun main() { |
3.5 Operator Overloading
operator keyword can re-define calculation way as you want.
1 | data class Point(val x: Int, val y: Int) |
unaryMinus() method is all of kotlin class would have. and you can overload it with operator keyword
3.6 lateinit keyword
non null properies in some class should be initialized in constructor block. but we sometimes encouter the case the parameters don’t have enough to call constructor at that time. we can use lateinit keyword to solve this problem. and you can often see this problen at the spring dependency injection (@Autowired)
1 | class OfficerControllerTests { |
val : immutable type
var : mutable type
3.7 overriding equals(), hashcode()
the one of the features of data class is that you should keep the equality. (it’s little different with equivalence.)
1 | class Customer(val name: String) { |
you can override equals(), hashCode() methods like the above.
3.8 creating singleton object
you should create singleton object like the below in Java
1 | public class Runtime { |
but the kotlin language offers the easy way to create singleton object unlike java.
1 | object MySingleton { |
you just change from class keyword to object keyword. then it will be created as the singleton type object. the below java code is the result of decompiled of MySingleton kotlin object.
1 | public final class MySingleton { |
3.9 Nothing class
1 | public class Nothing private constructor() |
the above code means that Nothing instance cannot be existed. because as you can see the above code, the constructor of Nothing class declared as a private type. and anywhere in Nothing class don’t create new instance. so Nothing instance is not existed in real.
you can use this type for the value never is existed.
4.1 fold
1 | inline fun <R> Iterable<T>.fold( |
fold function needs two parameters (initial and operation).
- initial : it’s initialization value.
- operation : it’s the way how to calculate them.
1 | fun sum(vararg nums: Int) = |
in lambda named operation, acc means a accumulated value and n means n-th value in nums array. the below is factoral function example with fold() function.
1 | fun factorialFold(n: Long): BigInteger = |
1 | fun fibonacciFold(n: Int) = |
and it is another example to use fold() function. especially in this function, n value in lambda doesn’t need so just mark it with ‘_’.
4.2 reduce
this function is fold() function without initial value.
1 | inline fun <S, T : S> Iterable<T>.reduce( |
1 | public inline fun IntArray.reduce( |
this code is real implementation code of reduce in IntArray collection.
1 | fun sumReduce(vararg nums: Int) = |
and it is the one of the example with reduce() function. this code calculate summation of nums array.
4.3 tail recursion
factorial calculation is usually implemented by recursion like the below.
1 | fun recursiveFactorial(n: Long): BigInteger = |
but recursiveFactorial() function will create new frame in call stack memory when it’s called. so someday it would encounter StackOverflowError. i mean this way would waste your memory. if you wanna optimize memory with recursive call then use tailrec.
1 |
|
this code is work well in 0L, 1L cases. because of @JvmOverloads annotation. I mean you can skip acc value if you want because it has default value (= BigInteger.ONE).
and tailrec keyword change your code from recursive style to loop style to optimize call stack memory when it’s compiled to Java.
5.1 array
The kotlin create the array with arrayOf() function easily.
1 | val strings = arrayOf("this", "is", "an", "array", "of", "strings") |
1 | fun <T> Array<out T>.withIndex() |
when you need to get index value in array you can use IndexedValue type with withIndex() function.
5.2 creating collection
- immutable collection : listOf, setOf, mapOf
- mutable collection : mutableListOf, mutableSetOf, mutableMapOf
5.4 creating map with collection
associate() function can create Map object from collection.
1 | val keys = 'a'..'f' |
5.5 returning default value when collection is empty
1 | data class Product(val name: String, var price: Double, var onSale: Boolean = false) |
5.8 destructuring list
1 | val list = listOf("a", "b", "c", "d", "e", "f", "g") |
you can get the fields of the object like (a, b, c, d, e).
5.9 sorting with multiple options.
1 | fun <T> Iterable<T>.sortedWith( |
1 | data class Golfer(val score: Int, val first: String, val last: String) |
comparator be more complexible then the below code is better to read than the above.
1 | data class Golfer(val score: Int, val first: String, val last: String) |
5.10 defining custom iterator
It is the definition of Iterator interface in kotlin.
1 | interface Iterator<out T> { |
It’s default way to use Iterator
1 | data class Player(val name: String) |
you can access it easier.
1 | operator fun Team.iterator() : Iterator<Player> = players.iterator() |
5.11 Filtering collection by type.
1 | val list = listOf("a", LocalDate.now(), 3, 1, 4, "b") |
1 | val list = listOf("a", LocalDate.now(), 3, 1, 4, "b") |
5.12 Create array with range.
1 | operator fun <T : Comparable<T>> T.rangeTo(that: T) = ClosedRange<T> = |
코틀린에서는 1..5 처럼 IntRange를 인스턴스화하는 2개의 점 연사자를 사용해서 범위를 생성한다.