by 연산자를 사용한 인터페이스 위임은 클래스가 인터페이스 호출을 특정 대리자 객체에 전달할 수 있게 해줍니다. 이는 코드 중복을 줄이고 조합 패턴(composition)을 구현합니다.
예제:
interface Logger { fun log(message: String) } class ConsoleLogger: Logger { override fun log(message: String) = println("LOG: $message") } class Service(logger: Logger): Logger by logger { fun doWork() { log("Service is working") } } val service = Service(ConsoleLogger()) service.doWork() // LOG: Service is working
속성 위임과의 차이점:
val/var x by ...)은 특정 속성에 적용되며, 대리자 인터페이스 (예: ReadWriteProperty)의 구현이 필요합니다.장점:
단점:
by를 사용한 인터페이스 위임은 객체를 필드를 통해 전달하여 인터페이스를 구현하는 것과 어떻게 다릅니까?
답변:
위임(by)은 대리자 객체를 통해 자동으로 모든 인터페이스 메서드를 구현합니다. 반면, 단순히 대리자 객체를 필드로 저장하고 수동으로 메서드를 호출하면 인터페이스의 각 메서드를 수동으로 작성해야 하며, 이는 중복 및 오류로 이어질 수 있습니다. 또한, by로 위임하면 가독성이 높아지고 보일러플레이트 코드가 줄어듭니다:
// 위임 없이 class Service2(private val logger: Logger): Logger { override fun log(message: String) = logger.log(message) }
이야기
프로젝트에서 Logger 인터페이스의 데코레이터 패턴을 수동으로 구현하려고 했는데, 추가된 인터페이스 메서드를 구현하는 것을 잊었습니다. 프로젝트는 컴파일되었지만, 새 기능은 작동하지 않았습니다. 왜냐하면 구현이 "빈 것"으로 존재했기 때문입니다. by를 통한 인터페이스 위임은 이 오류를 피할 수 있도록 해줬을 것입니다: 모든 새로운 메서드는 자동으로 대리자에 의해 구현됩니다.
이야기
by를 통해 인터페이스를 위임할 때, 개발자가 인터페이스 메서드 중 하나를 재정의했지만 나머지 메서드가 여전히 대리자를 통해 호출된다는 것을 잊었습니다. 결과적으로 일부 기능이 "비정상적"으로 작동하게 되었고, 비즈니스 메서드의 논리에서 이 오류는 오랫동안 포착되지 않았습니다.
이야기
여러 인터페이스를 by를 통해 위임하려고 했으나, 겹치는 메서드로 인해 충돌이 발생했습니다. 컴파일러가 모호성 오류를 발생시켰고, 중복된 메서드를 명시적으로 재정의해야 프로젝트가 빌드되었습니다.