곰돌푸우❤️

이 글은 https://kotlinlang.org/docs/reference/ 의 공식문서를 참고해서 한글로 차근차근 공부하며 정리한 글입니다. 아무쪼록 도움이 되시길 바라겠습니다.

Kotlin 컴파일 속도

  • 대개 Java보다 조금 빠르다!
  • 클린 빌드 시에는 Java보다 조금 느리지만, 일반적인 개발 시나리오의 증분 빌드 시 성능이 좋다.
  • Kotlin vs Java: Compilation speed

세미콜론 제거

fun main(args: Array<String>) {
    val age = 30
    val name: String = "Seo Jaeryong"
    println(name.length)
}

변수는 모두 val 아니면 var

  • val : 변경 불가능 (read-only)
fun main(args: Array<String>) {
    val age: Int = 30
    a = 20 // error
}
  • var : 변경 가능 (read-write)
fun main(args: Array<String>) {
    var age: Int = 30
    a = 20 // ok
}

함수 값 리턴의 간략화

  • 일반적인 함수
fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}
  • 간략하게 표현
fun maxOf(a: Int, b: Int): Int = if (a > b) a else b
  • 더 간략하게 표현 (리턴 타입을 생략해도 추론 가능)
fun maxOf(a: Int, b: Int) = if (a > b) a else b

Nullsafe

  • Kotlin은 널 참조의 위험(The Billion Dollar Mistake)을 제거하기 위해 노력했다.
  • 아래와 같은 상황에서만 NPE(NullPointerException)를 만들 수 있다.
    • 명시적인 throw NullPointerException() 호출
    • !! 오퍼레이터 사용
    • NPE를 발생시키는 외부 Java 코드 호출
  • Non-null
var a: String = "abc"
a = null // error
  • Nullable(?)
var b: String? = "abc"
b = null // ok
  • ‘?.’ operator (for nullable)
var b: String? = "abc"
b.length // error
b?.length // ok
  • 여러 체인의 객체를 호출할 때 유용하다.
// Java
String name;
if (bob != null) {
    if (department != null) {
        if (head != null) {
        name = bob.department.head.name;
    }
    }
}

// Kotlin
var name: String = bob?.department?.head?.name ?: "" // ok
  • !! operator (for nullable)
    • for NPE-lovers.
var b: String? = null
val l = b!!.length // ok, but throw an NPE if b is null
  • Safe casts
val aInt: Int? = a as? Int // return `null` if the attempt was not successful
  • Collections of Nullable Type
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()

접근자

  • public (default) : 전역 프로젝트에 공개
  • private : 같은 파일내에 공개
  • protected : Subclasses에 공개
  • internal : 같은 Module내에 공개
Module이란?
- IntelliJ an IntelliJ IDEA module
- a Maven project
- a Gradle source set
- a set of files compiled with one invocation of the Ant task.

자동 형변환 (Smart Casts)

  • is 체크 후 (Java의 instanceof)
fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x가 자동으로 String으로 형변환 된다.
    }
}
  • null 체크 후
fun demo1(x: String?) {
    if (x != null) {
        demo2(x) // x가 자동으로 NonNull String으로 형변환 된다.
    }
}

fun demo2(x: String) {
    print(x.length)
}

For-Loop

  • List
val items = listOf("apple", "banana", "kiwi")
for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

결과
item at 0 is apple
item at 1 is banana
item at 2 is kiwi
  • Range (a…b)
for (i in 0..10) { 
    print(i)
}

결과
012345678910

While-Loop

val items = listOf("apple", "banana", "kiwi")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

결과
item at 0 is apple
item at 1 is banana
item at 2 is kiwi

When

  • 다양한 타입 비교
when (obj) {
    1          -> "One"
    "Hello"    -> "Greeting"
    is Long    -> "Long"
    !is String -> "Not a string"
    else       -> "Unknown"
}
  • {} 블록을 지정해서 작성
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // Note the block
        print("x is neither 1 nor 2")
    }
}
  • 한 조건에 여러 값을 비교 (0, 1)
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}
  • 범위 비교
when (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("none of the above")
}

infix notation(중위 표기법)

  • 함수앞에 infix를 붙인다.
  • 멤버함수 혹은 확장 함수(extension funtions)에 사용
  • 하나의 파라미터를 받는 함수에서 사용
// 정의 방법
infix fun Int.shl(x: Int): Int {
    ...
}

1.shl(2)
1 shl 2 // can also be called like this

Extensions

  • 클래스에 함수 확장
// ViewExt.kt
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// SearchActivity.kt
var textView = findViewById(R.id.textView) as TextView
textView.show() // ok
textView.hide() // ok

클래스

  • 기본
class Invoice {

}
  • 바디가 없을 때 {} 생략가능
class Empty
  • 생성자 표현 (constructor 키워드)
class Person constructor(firstName: String) {

}
  • 생성자 표현에 constructor 키워드 생략 가능
class Person(firstName: String) {

}
  • 생성자의 초기화 블록 지정 (init 키워드)
class Customer(name: String) {
    init {
        logger.info("Customer initialized with value ${name}")
    }
}
  • 위 표현을 아래와 같이 표현 가능 (동일)
class Customer {
    constructor(name: String) {
        logger.info("Customer initialized with value ${name}")
    }
}
  • @Inject 어노테이션이 필요하면 constructor 키워드가 필요하다.
class Customer public @Inject constructor(name: String) { ... }
  • Data 클래스
    • 모든 var, val 변수의 Getter 제공
    • 모든 var 변수의 Setter를 제공
    • equals() / hashCode() / toString() / copy() 구현을 아름답게 제공
data class Customer(val name: String, var email: String)
  • Open 클래스
    • 코틀린의 모든 클래스는 기본적으로 final이라 상속이 불가능하다.
    • open 키워드를 class 앞에 붙여줌으로써 상속을 허용시킨다.
open class Base(p: Int)

class Derived(p: Int) : Base(p)
  • Abstract 클래스 (open 붙일 필요 없음)
abstract class Base {
    abstract fun f()
}

class Derived() : Base() {
    override fun f() {
        // ...
    }
}
  • Nested 클래스
    • Outer클래스 멤버 참조가 불가능
class Outer {
    private val bar: Int = 1
    class Nested {
        fun foo() = 2 // bar 참조 불가
    }
}

val demo = Outer.Nested().foo() // == 2
  • Inner 클래스
    • Outer클래스 멤버 참조 가능
class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar // bar 참조 가능
    }
}

val demo = Outer().Inner().foo() // == 1
  • 익명 Inner 클래스
    • object키워드를 사용하고 타입은 interfaceabstract class를 받는다.
    • interface : 이름 뒤에 ()를 붙이지 않는다. View.OnClickListener
    • abstract class : 이름 뒤에 ()를 붙인다. SimpleOnQueryTextListener()
// interface
button.setOnClickListener(object : View.OnClickListener {
    override fun onClick(view: View) {
        // ...
    }
})

// abstract class
searchView.setOnQueryTextListener(object : SimpleOnQueryTextListener() {
    override fun onQueryTextSubmit(query: String): Boolean {
        presenter.searchImage(query)
        return false
    }
})
  • Enum 클래스
enum class Direction {
    NORTH, SOUTH, WEST, EAST
}
enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}
  • 클래스 위임 (Class Delegation)
    • 해당 클래스안에 by절 에 오는 참조가 private으로 저장된다.
    • 해당 클래스안에 by절 에 오는 인터페이스의 메소드를 자동 생성한다.
interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// b가 Derived내에 private으로 저장 됨
// Base의 메소드를 Derived내에 자동 생성한다.
// 그 메소드들은 b를 참조하여 실행한다.
class Derived(b: Base) : Base by b 

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // prints 10
}

Destructuring Declarations

// class
data class Person(val name: String, val age: Int)

val (name, age) = Person("Jee-ryong", 30)
print("My name is $name and I am $age years old.")

// map
for ((key, value) in map) {
    print("key is $key")
    print("value is $value")
}

List

  • mutableListOf, arrayListOf 둘 다 ArrayList를 만들어 리턴
  • ArrayList보다 kotlin스타일로 리스트를 다루는 인터페이스가 구현되어 있는 MutableList를 사용하는 편이 더 나아보임.
val lists: List<Int> = listOf(1, 2, 3) // read only
val lists: MutableList<Int> = mutableListOf(1, 2, 3) // read/write
val lists: ArrayList<Int> = arrayListOf(1, 2, 3) // read/write

Map

// new instance
val map = mapOf("Korea" to 1, "Japan" to 2) // read only
val map = mutableMapOf("Korea" to 1, "Japan" to 2) // read/write
val map = linkedMapOf("Korea" to 1, "Japan" to 2)
val map = hashMapOf("Korea" to 1, "Japan" to 2) 
val map = sortedMapOf("Korea" to 1, "Japan" to 2)

// use
map.put("London", 3)
map.get("Korea")
map["Korea"]
map.containsKey("Japan")
map.toList()
map.toMap()
map.toMutableMap()
map.toSortedMap()

Set

// new instance
val set = setOf("Korea", "Japan")
val set = mutableSetOf("Korea", "Japan")
val set = hashSetOf("Korea", "Japan")
val set = linkedSetOf("Korea", "Japan")
val set = sortedSetOf("Korea", "Japan")

// use
set.add("London")
set.remove("London")
set.contains("London")
set.size
set.toList()
set.toMutableList()
set.toSet()
set.toHashSet()
set.toMutableSet()
set.toSortedSet()

Ranges

for (i in 1..4) print(i) // prints "1234"
for (i in 1..4 step 2) print(i) // prints "13"
for (i in 4 downTo 1) print(i) // prints "4321"
for (i in 4 downTo 1 step 2) print(i) // prints "42"
for (i in 1 until 10) println(i) // prints "123456789"
(1..12 step 2).last // 11

Equality

  • Referential equality (===, !==)
val a = Integer(10)
val b = a
a === b // true
a !== b // false

val a = Integer(10)
val b = Integer(10)
a === b // false
a !== b // true
  • Structural equality (==, !=)
data class Person(val name: String, val age: Int)
val person = Person("Jae-ryong", 20)
val person2 = Person("Jae-ryong", 20)
person == person2 // true
person != person2 // false
val hobbies = arrayOf("Hiking", "Chess")
val hobbies2 = arrayOf("Hiking", "Chess")
assertTrue(hobbies contentEquals hobbies2) // passed

// 참고 - contentEquals는 미리 정의 된 infix함수이다.
public infix inline fun <T> kotlin.Array<out T>.contentEquals(other: kotlin.Array<out T>): kotlin.Boolean

Lambdas

// example
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = arrayListOf<R>()
    for (item in this)
        result.add(transform(item))
    return result
}
  • input 파라미터 네이밍은 자유
var ints = listOf(1,2,3,4,5)
val doubled = ints.map { value -> value * 2 }
  • it을 사용하면 input 파라미터 생략가능
ints.map { it * 2 }
  • 사용하지 않는 파라미터는 _로 선언 가능
var map = mapOf("Korea" to "Seoul", "Japan" to "Tokyo")
map.forEach { _, value -> println("$value!") }
반응형
facebook twitter googleplus kakaostory naver