Java에서 어려운 델리게이션
interface Worker { fun work() fun takeVacation() } class Manager(val worker: Worker) { fun work() = worker.work() fun takeVacation() = worker.takeVacation() }
- 위와 같이 델리게이션을 이용하면 Worker에 추가적인 메서드가 추가된다면 Manager 도 그에 맞게 호출하는 코드를 추가해주어야 한다. ⇒
OCP 위반
코틀린의 by 키워드를 사용한 델리게이션
class Manager() : Worker by JavaProgrammer()
- 위와 같이 작성 시, Worker 인터페이스에 구현되어 있는 메서드를 코틀린 컴파일러가 Manager 클래스에 생성. 호출 시 → JavaProgrammer의 인스턴스로 호출을 요청함
- <인터페이스> by <해당 인터페이스를 구현한 클래스>
파라미터에 위임하기
단점
- 위와 같이 사용하면 델리게이션을 JavaProgrammer 에게만 할 수 있음
- Worker 인터페이스에 있지 않은 새로운 메서드를 작성해서 위임이 불가능함
class Manager(val staff: Worker): Worker by staff { fun meeting() = println("organizing meeting with ${staff.javaClass.simpleName}") }
메소드 충돌 관리
interface Worker { fun work() fun takeVacation() fun fileTimeSheet() = println("Why? Really?") } interface Assistant { fun doChores() fun fileTimeSheet() = println("No escape from that") } class Manager(val staff: Worker, val assistant: Assistant) : Worker by staff, Assistant by assistant { override fun takeVacation() = println("of course") override fun fileTimeSheet() { println("manually forwarding this...") assistant.fileTimeSheet() } }
- 위와 같이 Manager 클래스에서 fileTimeSheet() 메서드를 오버라이드 하지 않았다면 두 인터페이스에 모두 포함되어있는 해당 메서드로 인해 컴파일 오류가 발생함
델리게이션의 주의사항
- 델리게이션을 사용하는 클래스는 위임할 인터페이스를 구현해야 함. 그래서 델리게이션을 사용하는 클래스를 참조하면 위임할 인터페이스의 참조에 할당될 수 있다.
- var 보다는 val 을 사용하기. 델리게이션은 생성자에서 전달된 파라미터로 이용되는 것이지, 속성값 변경한다고 해서 델리게이션 자체가 바뀌지는 않음
val doe = Manager(JavaProgrammer()) println("Staff is ${doe.staff.javaClass.simpleName}") doe.work() // ...write Java println("changing staff") doe.staff = CSharpProgrammer() println("Staff is ${doe.staff.javaClass.simpleName}") doe.work() // ...write Java
변수와 속성 델리게이션
속성이나 지역 변수를 읽을 때, 코틀린 내부에서는 getValue() 함수를 호출함. 이와 유사하게 속성이나 변수를 설정할 때는 setValue() 함수를 호출
변수 델리게이션
지역변수의 읽기와 쓰기에 대한 접근을 모두 가로챌 수 있음. 그렇게 해서 리턴되는 것을 변경할 수 있고 데이터를 언제, 어디에 저장하는지도 변경할 수 있다.