3 minute read

1.Null safety

코틀린은 ‘non-nullable’을 기본으로 해 null 참조를 제거한다. 컴파일러가 초기화되지 않는 사용을 막는다. null 값을 가지고 싶으면 그 타입을 ‘nullable’로 선언해야 한다.

# nullable 선언
var nullable: String? = null

# nullable
//null check 후 실행
if (nullable != null) nullable.doSomething()

//Safe call
nullable?.doSomething()

# nullable 강제호출
//null 인지 non-null 인지 구분하지 않고 강제로 호출 null이면 NullPointException을 발생한다.
nullable!!.doSomething()

2.확장함수

클래스에 상속하거나 데코레이더 패턴을 사용하지 않고도 추가할 수 있다.

fun String.doSomethig(): String {

}

클래스 이름(String)을 수신 객체 타입(receiver type)이라 부르며, 수신 객체는 확장 함수에 접근할 떄 사용하게 되는 객체다. 확장 함수는 내부에서만 사용할 수 있는 private, protected 멤버에 접근할 수 없어서 캡슐화를 깰 수 없다.

3.Data class

대부분의 앱은 데이터 기반이라 스스로 데이터를 가지고 있기 위해 속성과 필드만 있는 클래스를 만든다. 자바에서 이 과정은 각 필드의 get/set 메서드를 요구하기 때문에 매우 귀찮고 번거롭다 코틀린은 클래스를 싱글라인으로 모든 속성과 함께 선언할 수 있다. 컴파일러가 모든 getter/setter를 만들고 toString(), copy() 메서드를 생성한다.

data class User(var name:String, var age: Int)

4.immutability

멀티스레드를 구현할 때 변수가 mutable하면 어떤 스레드에서 접근할 때 변화가능하고, 이것을 막기위해 동기화를 구현한다. 동기화는 복잡성과 실행시간을 증가시킴. 데이터가 변할 일이 없으면 멀티 스레드에서 에러없이 접근이 가능하다. 코틀린에서 변수는 var/val로 선언한다. var은 재설정 가능한 변수이고 val은 한번 설정되면 변할 수 없다. 자바에서는 비슷한 기능으로 final 키워드를 가지지만 var/val은 더 큰 의미를 가지는데 속성을 선언할 때 var은 getter/stter로 정의하는 반면, val은 getter와 private setter로 정의하고 항상 생성자에서 변수가 할당되어야 한다.

var animals = listOf("lion", "tiger")
animals.add("zebra") //컴파일 에러

5.범위 지정 함수 (apply, with, let, also, run)

5개의 함수는 전달받는 인자와 작동방식, 결과가 매우 비슷하기 때문에 많은 경우에 서로를 대체해서 사용할 수 있다. 이런 특성 때문에 상황에 따라 어떤 함수를 사용하는 것이 적절한 사용법인지 고민해 볼 필요가 있다.

# JAVA 텍스트 뷰에 대한 설정을 하기 위해 textView를 명시해줘야 한다.
TextView textView = findViewById(R.id.textView);
textView.setText("text")
textView.setSize(16)

# 범위 지정 함수의 수신객체가 매개변수로 암시적으로 전달된다.

# 코틀린 let은 지정된 값이 null이 아닌 경우에 코드를 실행할 수 있기 때문에 safe call을 할 수 있다.
textView?.let {
  it.text = "text"
  it.size = 16
}

# also also는 apply와 마찬가지로 수신 객체를 반환하지만 객체의 사이드 이팩트를 확인하거나, 수신객체의 프로퍼티에 데이터를 할당하기 전에 해당 데이터의 유효성을 검사할 때 사용.

class book(author: Person) {
  val author = author.also {
    requireNotNull(it.age)
    print(it.name)
  }
}

# apply 람다 내부에서 수신 객체의 함수를 사용하지 않고 수신 객체 자신을 다시 반환하려는 경우 사용.
val person = Person().apply {
  name = "sonsation"
  age = 27
}

# run 어떤 값을 계산할 필요가 있거나 여러개의 지역 변수의 범위를 제한할 때 사용.
val inserted: Boolean = run {
    val person: Person = getPerson()
    val personDao: PersonDao = getPersonDao()
    personDao.insert(person)
}
fun printAge(person: Person) = person.run {
    print(age)
}

# with Non-nullable 수신 객체 이고 결과가 필요하지 않은 경우에만 사용.
val person: Persion = getPerson()
  with(person) {
    print(name)
    print(age)
  }
}

범위 지정 함수는 코드 가독성을 향상시키기 좋다. 하지만, 원칙적으로 중첩은 하지 않는게 좋다. 이 함수들은 수신 객체를 this또는 생략하며, 수신객체의 이름을 다르게 지정할 수 없기 때문에 중첩될 경우 혼동하기 쉬워진다.

6.Higher-order functions and lambdas

고차 함수는 함수를 파라미터로 가져오고 함수를 리턴할 수 있다. 주요 유스케이스는 콜백이며, 자바에서 인터페이스를 생성하고 한쪽에서 인터페이스를 구현해야 하는 반면에 코틀른에서는 함수를 변수에 저장할 수 있어 나중에 사용할 수 있다. 또는 또 다른 함수 안에서 생성될 수 있다. 함수가 선언되지 않았는데 전달되는 것을 람다 또는 익명 함수라고 한다.

# 변수로 함수 넘기기
fun calc(x: Int, y:Int, operation: (Int, Int)->Int) = operation(x, y)
run { println(calc(5,5, {x, x -> x+x})) }
//실행결과 : 10

# 함수 리턴
fun getCalcFunctions(action: Int): (Int, Int)->Int {
  when(action)
    1 -> return {x: Int, y:Int -> x+y}
    2 -> return {x: Int, y:Int -> x-y}
}

val calFunction = getCalcFunctions(2)
run { println(calFunction(5, 5)) }
//실행결과 : 0

# 코드의 간결성을 위해 규칙이 존재
# 1. 함수의 맨 마지막 인자가 람다라면() 안에서 뺴내서 밖에 람다를 표현할 수 있다.
# 2. 인자가 하나라면 그 인자는 람다식 내부에서 'it'으로 받을 수 있다.
# 3. 인자가 하나이면서 그 인자가 람다타입 이라면 ()을 생략할 수 있다.

people.maxBy ({p: Person -> p.age})
people.maxBy () {p: Person -> p.age}
people.maxBy {p: Person -> p.age}
people.maxBy {it.age}

Categories:

Updated: