Action Kotlin 3 - 클래스, 객체, 인터페이스

Interface

선언하기

click이라는 추상 메서드가 있는 인터페이스를 다음과 같이 작성할 수 있다.

interface Clickable{
    fun click()
}

구현하기

Kotlin에서는 :을 통해 클래스 확장과 인터페이스 구현이 가능하다.

class Button : Clickable{
    override fun click() = println("click!")
}

중요한 점은, Interface의 경우 개수 제한 없이 구현이 가능하지만, 클래스의 확장은 오직 하나만 가능하다.

디폴트 구현

java에서는 디폴트 메서드 선언시 default를 작성해주어야 했지만, Kotlin에서는 바로 작성해 주면 된다.

default 메서드는 구현을 원한다면 새로운 동작을 정의할 수 있고, 아니면 정의된 구현을 사용해도 된다.

interface Clickable{
    fun click() = println("click!")
}

두 인터페이스에 같은 디폴트 메서드를 함께 구현

위에서, 한 class 에서는 interface를 개수 제한 없이 생성할 수 있다고 했다.
그렇다면, 동일한 디폴트 메서드를 구현하는 다른 인터페이스가 존재하고 이러한 두 개의 인터페이스를 상속받은 상황에서, 디폴트 메서드를 호출했을 때 어떤 것이 호출될까?

정답은 어느쪽도 선택되지 않는다이다.
오버라이드 메서드를 직접 제공하지 않으면 컴파일 오류가 발생한다.

다음과 같이 상위 타입의 이름을 <> 사이에 넣어주고, super를 지정하면 된다.

super<상위 타입> 메서드 

Class

Open Class

Kotlin에서는 클래스와 메서드는 기본적으로 final이다.
상속을 허용하고 싶다면, open변경자를 붙여야 한다.

open class RichButton : Clickable{
    fun disable() {}
    open fun animate() {}
    override fun click() {}
}

disable함수는 final이다. 하위 클래스에서 오버라이드가 불가능하다.
animate함수는 open이므로 오버라이드해도 된다.
override가 붙게 되면, 기본적으로 열려있다.

만약 override하는 메서드의 구현을 하위에서 못하게 하려면, final을 붙이면 된다.

protected

java에서의 protected는 같은 패키지 안에서 접근이 가능하다.
kotlin에서 protected는 오직 어떤 클래스나 그 클래스를 상속한 클래스 안에서만 보인다.

sealed class

when을 사용할 때 else를 이용하여 아닌 경우를 처리해주어야 한다.
항상 이러한 default분기를 추가하는 것은 번거롭다.

sealed class를 이용해 해결이 가능하다.
상위 클래스 앞에 sealed를 붙이면, 상속한 클래스의 정의를 제한할 수 있다.

sealed class Expr{
    class Num(val value : Int) : Expr()
    class Sum(val left : Expr, val right : Expr) : Expr()
}
fun eval(e :Expr) : Int = 
    when(e) {
        is Expr.Num -> e.value
        is Expr.Sum -> eval(e.right) + eval(e.left)
    }
}

sealed클래스는 when이 모든 하위 클래스를 검사하므로, 별도의 else가 필요없다.

sealed는 자동으로 open이다.

== 와 ===

java 에서 equals==의 차이를 먼저 설명하면,
equals는 객체의 내용을 비교한다.

String str1 = new String("Hello");
String str2 = new String("Hello");

System.out.println(str1.equals(str2)); // true (문자열 내용이 동일)

다음과 같은 상황에서 두 객체의 내용이 모두 Hello이기에 true를 반환한다.

반면 ==의 경우에는 객체의 참조 비교를 수행한다.

String str1 = new String("Hello");
String str2 = new String("Hello");

System.out.println(str1 == str2); // false (두 객체는 서로 다른 메모리 위치에 저장됨)

두 객체는 서로 다른 메모리 위치에 저장된다, 결과는 false를 반환한다.

그렇다면 Kotlin에서는 어떨까?

Kotlin에서는 =====로 가능하다.
Kotlin에서는 반대로 ==는 객체의 내용을 비교한다.

val str1 = "Hello"
val str2 = "Hello"
println(str1 == str2) // true (두 문자열의 내용이 동일)

다음과 같은 상황에서kotlintrue를 반환한다.

만약에 객체의 참조 비교를 하고 싶다면, ===를 사용하면 된다.

val str1 = "Hello"
val str2 = str1
println(str1 === str2) // true (str1과 str2는 같은 문자열 객체를 가리킴)

만약에 클래스를 ==로 비교한다고 한다면, equals()오버라이드하여 원하는 동등성을 비교해야한다.

equals의 인자는 Any이다. Any는 모든 클래스의 상위 클래스이다.

fun equals(other: Any?): Boolean

Object

객체 선언

object decoration은 싱글턴을 정의하는 방법이다.

동반 객체

companion object는 인스턴스 메서드는 아니지만 어떤 클래스와 관련 있는 메서드와 팩토리 메서드를 담을 때 쓰인다.

객체 선언 : 싱글턴을 쉽게 만들기

Kotlin에서는 객체 선언기능 (object)를 통해 싱글턴을 언어에서 기본 지원한다.
객체 선언은 클래스 선언과 클래스에 속한 단일 인스턴스의 선언을 합친 선언이다.

싱글턴 객체는 생성자가 필요가 없다.
객체 선언문이 있는 위치에서 생성자 호출 없이 만들어진다.

Object의 상속

object도 클래스나 인터페이스 상속이 가능하다.

동반 객체 : 팩토리 메서드와 정적 멤버가 들어갈 장소

코틀린 언어는 static 키워드를 지원하지 않는다.
대신에 최상위 함수를 활용하는 편을 권장한다.

하지만, 최상위 함수의 경우 private 클래스의 비공개 함수를 접근할 수 없다.
그렇기에, 클래스 내부 정보에 접근해야 하는 함수가 필요할 때는 클래스에 중첩된 객체 선언의 멤버 함수로 정의해야 한다.