이번시간에는 코틀린의 프로퍼티와 인터페이스에 대해서 알아보자. 프로퍼티 경우에는 조금 다른점이 있는데 인터페이스같은 경우에는 자바와 거의 비슷하므로 간단하게만 설명해보겠다.

프로퍼티

프로퍼티 선언

class Product {
    val id :Long? = null
    val name : String? = null
    val price : BigDecimal? = null
}

null은 컴파일 에러만 피하기 위해 작성하였다. 여기서는 중요한게 아니므로 주의깊게 보지 않아도 된다. 실제 저런 코드는 거의 사용할 일이 없을 듯하다.
코틀린의 경우에는 위와 같은 형식으로 프로퍼티를 작성할 수 있다. val 로 선언된 경우에는 읽기만 가능하고 var 로 선언한 것은 쓰기 읽기 모두 가능하다.

var product = Product()
product.name = "iphone 7" //컴파일 에러

위와 같이 Product를 생성 후에 name에 접근해서 어사인을 할 경우에는 컴파일 에러가 발생한다. 위의 코드를 쓰기도 가능하게 하려면 프로퍼티 선언을 다음과 같이 해야한다.

class Product {
    var id :Long? = null
    var name : String? = null
    var price : BigDecimal? = null
}

한마디로 getter만 사용하고 싶을 경우에는 val getter setter 모두 사용하고 싶을 때는 var로 선언 하면 된다.
위의 경우에는 일반적인 방법이다. 위와 같이 일반적으로 할당한 값을 넣고 빼고 할 수도 있겠지만 그렇지 않은 경우도 있다.
예를들어 Product 클래스의 name 프로퍼티에 무조건 # 해쉬 값을 넣고 싶다고 가정해보자. 그럼 우리는 조금 커스텀하게 getter나 setter를 만들어야 된다.

getter와 setter

class Product {
    var id :Long? = null
    var name : String? = null
        set(value) {
            field = "#" + value
        }
    var price : BigDecimal? = null
}

위와 같이 name 프로퍼티 아래에 set이라는 키워드를 사용하면 된다. set이라는 키워드에 파라미터는 굳이 value라는 변수로 하지 않아도 된다. 코틀린의 권장사항일 뿐이지 다른 변수명으로 사용해도 상관은 없다. 그리고 뜬금 없이 field 라는 필드가 보인다. 저 변수는 getter와 setter 안에서만 사용할 수 있는 Backing 필드라고 부른다. 저 필드가 실제 위에서는 name이라는 필드에 어사인을 하는 그런 필드 인 듯 싶다.
위에서는 setter만 이용해서 값을 넣어지만 이번에는 getter로 가져올 때 # 해쉬를 붙어서 가져와 보자.

class Product1 {
    var id: Long? = null
    var name: String? = null
        get() {
            return "#" + field
        }
    var price: BigDecimal? = null
}

setter와 마찬가지로 get이라는 키워드를 사용해서 getter를 커스텀하게 만들 수 있다. getter의 경우에는 expression 으로도 만들 수 있다.

var name: String? = null
    get() = "#" + field

expression으로 좀 더 간단하고 깔끔해 진 듯하다. 하지만 setter의 경우에는 expression으로 만들 수 없는 듯하다. 해보니까 컴파일 에러가 발생한다. 가능하게 만들 수 있을 지는 모르겠으나 일단 현재는 잘 모르겠다.

접근제한자 및 어노테이션

setter나 getter에 접근제한자 및 어노테이션을 선언 할 수 있다.

var name: String? = null
    private set(value) {
        field = "#" + value
    }

위와 같이 set 키워드 앞에 접근제한자 private를 선언 하고 있다. 그러면 자바와 동일하게 현재 클래스 외에 다른 클래스들은 name에 접근하여 값을 어사인 할 수 없다.

var name: String? = null
    private get() = "#" + field

위와 같이 getter에도 할 수 있을 거라고 생각했지만 위의 코드는 컴파일 에러가 발생한다. 그 이유는 필드와 getter의 접근제한자가 다르기 때문이다. 그래서 필드에도 private를 선언해줬다.

private var name: String? = null
    private get() = "#" + field
    set(value) {
        field = "#" + value
    }

하지만 필드에 접근제한자를 사용하면 그 아래에 있는 getter setter 모두가 필드의 접근제한자를 따라간다.

var product = Product1()
product.name = "iphone 7" //컴파일 에러
println(product.name)     //컴파일 에러

위의 코드는 어사인할 때 도 컴파일 에러가 나며 가져올 때도 컴파일 에러가 발생한다.

@Inject set(value) {
    field = "#" + value
}

위와 같이 어노테이션도 사용할 수 있다.

상수

컴파일 타임에 값을 알 수 있는 프로퍼티는 const라는 키워드를 사용하면 된다.

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() {  }

위와 같이 어노테이션에서도 사용할 수 있다.

초기화 지연

보통 non-null 타입을 갖는 프로퍼티를 선언하면 생성자에서 초기화해야 한다. 물론 개발을 할 때는 초기화 해주는 것이 좋지만 테스트할 경우에는 늘 좋은 것만은 아니다. 예를 들어 의존성 주입이나 단위 테스트를 통해 setup 메서드에서 프로퍼티를 초기화 할 수 도 있다. 그때 사용할 수 있는 키워드가 lateinit이라는 키워드이다.

class MyTest {
    lateinit var subject: TestSubject
    @SetUp 
    fun setup() {
        subject = TestSubject()
    }

    @Test 
    fun test() {
        subject.method() // null 검사 없이 직접 접근
    }
}

위와 같이 초기화를 하지 않아도 사용 할 수 있다. 하지만 이 경우에는 val 키워드를 사용할 수 없고 var 키워드만 사용 가능하다. 만약 초기화되기 전에 lateinit 키워드가 사용된 프로퍼티에 접근하면 초기화되지 않은 프로퍼티에 접근했음을 정확하게 알려주기 위해 특수한 익셉션 을 발생한다.

인터페이스

코틀린의 인터페이스는 java8과 아주 비슷하다. 그래서 java8을 사용하는 개발자라면 쉽게 배울 수 있을 듯하다. 자바와 마찬가지로 추상클래스와 인터페이스의 차이는 상태를 가질 수 없다.

interface MyInterface {
    fun bar()
    fun foo() {
      //함수 바디
    }
}

자바와 동일하게 interface라는 키워드를 사용하면 된다. java8과 동일하게 함수의 바디를 가질 수 있다.

인터페이스 구현

class MyClass : MyInterface{
    override fun bar() {

    }
}

class 명 뒤에 :를 사용해서 구현할 수 있다. 자바와 마찬가지로 한개 이상의 인터페이스를 구현할 수 있다.

인터페이스의 프로퍼티

interface MyInterface {
    val price :Int
    fun bar()
    fun foo() {
        println(price)
    }
}

위와 같이 인터페이스에 프로퍼티를 선언 할 수 있다. 인터페이스의 선언된 프로퍼티는 추상프로퍼티이므로 구현체에서 구현을 해주어야 한다.

class MyClass : MyInterface{
    override val price: Int = 100
    override fun bar() {

    }
}

위와 같이 구현클래스에서 price라는 프로퍼티를 구현?(선언)해야 한다.
인터페이스의 프로퍼티를 제외 하곤 자바의 인터페이스가 거의 동일하므로 나머지는 생략하겠다.

이렇게 오늘은 코틀린의 프로퍼티와 인터페이스에 대해 살펴 봤다. 다음 시간에는 data class와 object에 대해서 살펴보기로 하겠다.