특정 인터페이스에 기능을 확장하고 싶을 때 기존에 잘 구현된 구현 클래스를 바탕으로 편하게 확장하고 싶을 때 사용할 수 있다.
대표적으로 데코레이터 패턴에 by 키워드를 이용할 수 있다. 확장할 기능 외의 함수 구현은 다른 객체에게 맡기고, 원하는 메서드만 오버라이딩한다.
그러면 다른 추상 메서드를 오버라이딩 할 필요가 없기 때문에
IDE 도움 없이도 편하게 추상 메서드를 구현할 수 있으며 코드베이스를 깔끔하게 유지할 수도 있다.
class MyList<T>(
private val innerList: MutableList<T> = arrayListOf()
) : MutableList<T> by innerList {
/*
MutableList의 수많은 추상 메서드를 일일이 나열하지 않아도 된다.
그럼에도 MyList의 인스턴스로 MutableList의 모든 인터페이스를 사용할 수 있다.
그 구현을 innerList에게 위임한 것이다.
*/
}
object의 사용처는 여러개지만 클래스를 정의함과 동시에 인스턴스를 생성한다는 공통점이 있다.
싱글턴 클래스를 정의할 때 사용한다. 클래스 정의 방식과 같으며 클래스나 인터페이스를 확장할 수 있다.
자바에서 코틀린 object 객체를 사용하고 싶으면, [object 이름].INSTANCE.[멤버] 형식으로 한다.
object MyObject : Parent() {
}
object 앞에 companion을 붙이면 특별한 객체가 생성된다. 클래스의 이름은 따로 붙이지 않아도 된다.
그러면 companion object를 감싼 클래스의 이름으로 객체를 참조할 수 있으며, 말 그대로 바깥 클래스의 동반 객체가 된다.
이곳에서는 private constructor에 접근할 수 있다. 또한, 자바의 static 역할을 그대로 구현할 수 있다. 따라서, 팩토리 메서드를 구현하기에 적합하며 그런 목적으로 많이 사용된다.
생성자에 이름을 붙일 수 있다.
이미 존재하는 정보(동등한 data class 객체)에 대한 인스턴스를 생성하려고 할 때, 새로 생성하지 않고 캐시에서 이미 존재하는 객체를 반환할 수 있다. 즉, 동등성이 있는 객체를 중복 생성하지 않게 할 수 있다.
팩토리 메서드는 final 클래스에 사용하기 적합하다. 하위 클래스에서 생성자를 오버라이딩 해야 할 수도 있으니까.
ex) Person.Companion.멤버
object로 무명 객체를 정의 및 생성할 수 있다. 자바의 무명 내부 클래스에 해당한다.
그렇게 무명 객체를 생성하는 식을 object expression이라고 한다.
자바와 달리 여러 인터페이스를 구현할 수 있고, 추상 클래스와 인터페이스를 동시에 상속할 수 있다. final이 아닌 변수 필드를 가질 수도 있다.
해결) 말도 안되지 실제로 아니기도 하고. 접근할 수 있는 private member는 construrctor가 유일하다.
해결) sealed class의 상속은 open이며 가시성은 자바의 protected와 같다. 즉, 같은 패키지 안에서만 사용 가능하다.
해결) 접근 권한. 즉, 사용 권한을 의미한다.
해결) 잘 설계해도 인스턴스 생성처럼 어딘가에서 한 번은 하위 타입에 따라 분기해야 한다. 그리고 항상 조건문을 없앨 수 있는 게 아니다.