반응형

아이템 1: 가변성을 제한하라

코틀린은 모듈로 프로그램을 설계한다. 모듈은 클래스, 객체, 함수, 타입 별칭(type alias), 톱레벨(top-level) 프로퍼티 등 다양한 요소로 구성된다. 이러한 요소중 일부는 상태(가변성)를 가질 수 있다. mutable 객체를 사용하거나, var 프로퍼티를 사용하거나.... 

요소가 상태를 갖게 되면 시간(이력)에 따라 값이 변하기 때문에 바람직하지 않다.

그래서 코틀린은 가변성을 제한할 수 있게 설계되어 있다. 대표적인 방법은 아래와 같다.

  • val / var
  • Immutable / Mutable 
  • data class copy() / data class

한눈에 볼 수 있게 Immutable / Mutable 형식으로 정리했다.

정리하자면

  • 가능하다면 Mutable보다는 Immutable을 사용하는 것이 좋다.
  • Mutable객체는 외부에 노출하지 않는 것이 좋다.

 

아이템 2: 변수의 스코프를 최소화하라

 

아이템 3: 최대한 플랫폼 타입을 사용하지 말라

 

아이템 4: inferred 타입으로 리턴하지 말라

 

아이템 5: 예외를 활용해 코드에 제한을 걸어라

확실하게 어떤 형태로 동작해야 하는 코드가 있다면, 예외를 활용하는 것이 좋다. 코틀린에서는 코드의 동작에 제한을 걸 때 다음 방법을 쓸 수 있다.

  • require 블록 : 값을 제한
  • check 블록 : 상태와 관련된 동작을 제한

블록을 사용해 예외를 처리하면 다음과 같은 장점이 있다.

  • 리팩토링에 용이하다.
  • 예상하지 못한 동작을 실행하지 않는다.
  • 단위 테스트를 줄일 수 있다.
  • 타입 변환을 줄일 수 있다.

 

require 

fun factorial(n: Int){
    // require example
    require(n>=0)
    require(n>=0){ "Cannot calculate factorial of $n. Because it's smaller than 0."}
    
    // require not null example
    requireNotNull(n)
    requireNotNull(n){ "Cannot calculate factorial of $n. Because it's null"}
}

require/requireNotNull은 위 코드처럼 사용한다.

require는 제한을 확인하고, 제한을 만족하지 못할 경우 예외를 throw한다.

함수로만 사용하면 IllegalArgumentException을 throw하고, 람다를 활용해서 지연메세지를 정의할 수도 있다.

 

check

// check example
fun speak(text: String){
    check(isInitialized)
    check(isInitialized){ "Cannot work. Because it's not initialized"}
}

// check not null example
fun getUserInfo(): UserInfo{
    checkNotNull(token)
    checkNotNull(token){ "Cannot work. Because it's null"}
}

check/checkNotNull은 위 코드처럼 사용한다.

check는 require와 비슷하지만, 지정된 예측을 만족하지 못할 때 예외를 throw한다.

함수로만 사용하면 IllegalStateException을 throw하고, 람다를 활용해서 지연메세지를 정의할 수도 있다.

 

nullability와 스마트 캐스팅

코틀린에서는 require(), check() 블록으로 true가 나왔다면 블록 이후도 true라고 가정한다. 따라서 이를 잘 활용하면 스마트 캐스트가 작동한다.

class Person(val email: String?)

fun sendEmail(person: Person, message: String){
    // String? -> String smart-casting
    requireNotNull(person.email)
    val email = person.email
    // ...
}

 

또한 nullability를 목적으로, Elvis연산자를 활용하는 경우가 많다. 오른쪽에 throw 또는 return을 두고 사용한다.

fun sendEmail(person: Person, message: String){
    val email = person.email ?: return
    // ...
}

 

프로퍼티에 문제가 있어서 null일 때 여러 처리를 해야 할 때에, return/throw와 run 함수를 조합할 수도 있다.

fun sendEmail(person: Person, message: String){
    val email = person.email ?: run{
        Log.e("Email not sent, no email address")
        return        
    }
    // ...
}

 

아이템 6: 사용자 정의 오류보다는 표준 오류를 사용하라

위의 require, check를 사용하면 대부분의 코틀린 오류를 처리할 수 있지만, 예측하지 못한 상황에 Exception이 발생할 수도 있다. 일반적으로 사용되는 예외는 다음과 같다.

  • IndexOutOfBoundsException : 인덱스 파라미터 값이 범위를 벗어났을 때 나타남
  • ConcurrentModificationException : 동시 수정(concurrent midification)을 금지했는데, 발생했을 때 나타남
  • UnsupportedOperationException : 사용자가 사용하려고 했던 메서드가 현재 객체에서는 사용할 수 없을 때 나타남
  • NoSuchElementException : 사용자가 사용하려고 했던 요소가 존재하지 않을 때 나타남 (ex) 내부 요소가 없는 Iterable에 대한 next 호출)

아이템 7: 결과 부족이 발생할 경우 null과 Failure를 사용하라

 

아이템 8: 적절하게 null을 처리하라

 

아이템 9: use를 사용하여 리소스를 닫아라

 

아이템 10: 단위 테스트를 만들어라

 

반응형

+ Recent posts