Coding Log


Kotlin

본 카테고리는 2017년 Android 공식 언어로 채택된 Kotlin에 관하여 다룬다.

Kotlin을 이용해 개발하는 Android는 추후 따로 다루기로 하고 언어 자체에만 집중한다.

참고 kotlin 공식 사이트

Class - 데이터 클래스(Data Classes)

우리는 보통 데이터를 저장하기 위해서 클래스를 자주 만들게 된다.

데이터를 저장하기 위해 만드는 클래스는 일부 표준 기능과 유틸리티 관련 기능(함수)들은 데이터로부터 기계적으로 파생된다.

Kotlin은 이 클래스는 데이터 클래스 라고 부르며 data 키워드로 표시한다.

아래는 데이터 클래스의 예시이다.

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

컴파일러는 데이터 클래스의 기본 생성자에 선언한 모든 프로퍼티로부터 아래의 멤버를 자동으로 생성해준다.

  • equals() / hashCode() 
  • toString() 함수 (위의 예시 클래스로 치면 User(name=John, age=42) 형식)
  • 프로퍼티가 선언된 순서에 대응하는 componentN() 함수
  • copy() 함수

데이터 클래스는 아래의 조건을 충족함으로써 일관성있는 코드와 의미있는 기능을 제공할 수 있다.

  • 기본 생성자는 최소 1개 이상의 파라미터가 필요하다.
  • 모든 기본 생성자 파라미터는 val 혹은 var 타입이어야 한다.
  • 데이터 클래스는 추상, open, sealed, 내부 클래스로 구현할 수 없다.

추가적으로, 데이터 클래스 멤버의 생성은 아래의 규칙을 따른다.

  • 데이터 클래스나 수퍼 클래스에 final 타입으로 equals(), hashCode(), toString() 함수가 구현되어 있는 경우 구현된 코드로 동작된다.
  • 상위 타입에 open 키워드나 호환가능한 타입을 반환하는 componentN() 함수가 있는 경우엔 데이터 클래스는 이 함수에 대응하는 함수를 오버라이딩하여 생성한다. 만약 상위 타입의 함수와 형태가 맞지않거나 final로 정의된 경우엔 오버라이딩이 불가하므로 에러를 발생시킨다.
  • componentN() copy()를 직접 구현하는 것은 불가능하다.

참고 Kotlin 1.1버전 이후로 데이터 클래스는 다른 클래스를 상속받아 확장될 수 있다.

JVM 환경에서 생성된 클래스에서 파라미터없이 생성자를 사용하고 싶은 경우엔 아래와 같이 모든 속성의 기본값을 지정해야 한다.

data class User(val name: String = "", val age: Int = 0)

참고 Class - 클래스와 상속 포스팅

프로퍼티의 클래스 본문 내 선언(Properties Declared in the Class Body)

컴파일러는 자동으로 생성된 함수에 대하여 기본 생성자 내부에서 정의된 프로퍼티만 사용한다.

따라서 생성된 클래스 구현체에서 특정 프로퍼티를 제외하려면 클래스 본문에 직접 선언한다.

data class Person(val name: String) {
    var age: Int = 0
}

toString(), equals(), hashCode(), copy() 구현 내에서는 프로퍼티명만 사용되며, 이는 component1()이라는 하나의 컴포넌트 함수만 가지게 된다.

위의 예제 코드에서 두 개의 Person 객체를 생성하면, 각기 다른 age값을 가지지만 동등한 것으로 취급된다.

아래 예제를 통해 한 번 더 살펴보자.

data class Person(val name: String) {
    var age: Int = 0
}

fun main(args: Array<String>) {
    val person1 = Person("John")
    val person2 = Person("John")

    person1.age = 10
    person2.age = 20


    println("person1 == person2: ${person1 == person2}")
    println("person1 with age ${person1.age}: ${person1}")
    println("person2 with age ${person2.age}: ${person2}")
}

위의 코드를 실행하면 아래와 같은 결과를 출력한다.

person1 == person2: true
person1 with age 10: Person(name=John)
person1 with age 20: Person(name=John)

복사(Copying)

객체를 복사하는 경우에 프로퍼티의 일부만 변경하고 나머진 그대로 유지하는 경우가 종종 발생한다.

이 경우에 쓰기 위해 copy() 함수를 생성하여 사용할 수 있다.

data class User(val name: String = "", val age: Int = 0)

위의 User 데이터 클래스의 경우엔 아래와 같이 구현한다.

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)     

사용법은 아래를 참고하자.

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

데이터 클래스와 선언 분리(Data Classes and Destructuring Declarations)

데이터 클래스에 생성되는 컴포넌트 함수는 분리하여 선언할 수 있다.

아래 예제 코드를 통해 확인해보자.

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")

출력 결과는 아래와 같다.

Jane, 35 years of age

표준 데이터 클래스(Standard Data Classes)

표준 라이브러리는 Pair Triple을 제공한다.

이름이 있는 데이터 클래스를 사용함으로써, 의미있는 이름을 가진 프로퍼티를 선언하여 보다 가독성을 높여주기 때문이다.

DISQUS 로드 중…
댓글 로드 중…

트랙백을 확인할 수 있습니다

URL을 배껴둬서 트랙백을 보낼 수 있습니다