코틀린 (Kotlin) by 키워드 정리

코틀린 by 키워드

개발하다 보면 하위 클래스가 상위 클래스를 상속해서 상위 클래스 메소드를 오버라이드를 하는 경우가 많습니다.
이러한 상황에서 유지보수를 하다보면 상위 클래스가 변경이되는 경우 하위 클래스가 상위 클래스에 의존하고 있던 상황이 변경되면서 예기치 않은 오류가 발생합니다.

그래서 코틀린에서는 기본적으로 클래스는 final 입니다.
상속가능한 클래스는 open 을 해서 상속이 가능하다는 것을 알려줍니다.
상위 클래스 변경 시 하위 클래스에 영향을 줄 수 있다는 것을 인지시켜 줄수 있습니다.

by - 위임

상속을 허용하지 않는 클래스에 새로운 기능을 추가할때는 위임을 사용할 수 있습니다.
위임을 사용하면 상속하지 않고 기존 기능을 그대로 사용하면서 새로운 기능을 추가할 수 있습니다.
코틀린에서는 by 키워드를 사용하여 위임 기능을 사용할 수 있습니다.

//상속하지않고 새로운 함수 추가하기
class CountingSet<T>(
    val innerSet: MutableCollection<T> = HashSet()
) : MutableCollection<T> by innerSet { //MutableCollection 구현을 innerSet 에 위임

    var objectsAdded = 0 //카운트

    //override 함수는 HashSet() 대신 구현한 것을 사용     override fun add(element: T): Boolean {
    
    objectsAdded++    //add할때마다 카운트 증가
        return innerSet.add(element)
    }

    //override 함수는 HashSet() 대신 구현한 것을 사용     override fun addAll(c: Collection<T>): Boolean {
    
    objectsAdded += c.size  //add할때마다 카운트 증가
        return innerSet.addAll(c)
    }
}

위 코드에서 by innerSet 에서 innerSet 을 HashSet 이라고 가정합니다.
HashSet 클래스는 Collection 을 구현했기때문에 by innserSet 으로 선언하면 컴파일러가 자동으로 컴파일 시점에 HashSet 클래스의 모든 메소드를 자동으로 연결해 줍니다.
다만, override 된 함수인 경우 overrride 된 함수를 사용하도록 컴파일러가 따로 뭔가를 해주진 않습니다.

컴파일된 자바 파일을 보면 생성자에서 파라메터로 Collection innerSet을 받습니다.
그리고 implements Collection 으로 구현해야될 메소드들을 모두 구현합니다.
구현된 메소드에는 생성자에서 넘겨받은 innserSet 을 호출합니다.(위임)

결국 Collection 을 implements 하는것 처럼 보이지만 실제 호출은 넘겨받은 innerSet(HashSet) 의 메소드를 호출하는 브릿지 역할을 합니다.
예제로 HashSet 을 사용했지만, Collection 을 구현한 TreeSet 클래스도 가능합니다.

//컴파일된 자바 코드
public final class CountingSet implements Collection, KMutableCollection {
   private final Collection innerSet; //위임 객체 설정

   public CountingSet(@NotNull Collection innerSet) {
      Intrinsics.checkParameterIsNotNull(innerSet, "innerSet");
      super();
      this.innerSet = innerSet;
   }

   public boolean remove(Object element) {
      return this.innerSet.remove(element); //innserSet의 remove() 사용
   }

   public boolean contains(Object element) {
      return this.innerSet.contains(element); //innserSet의 contains() 사용
   }

   .... //구현해야될 다른 메소드들이 컴파일할 때 자동으로 생성 됨
}

이처럼 by 로 위임을 사용하면 상속이 불가능한 클래스에 새로운 기능을 추가하기위해 사용했던 데코레이터 패턴을 구현을 쉽게 해줍니다.
자바에서는 데코레이터 패턴을 구현하기위해 해당 클래스의 모든 메소드를 만들어서 연결해 줬지만, 코틀린에서는 by 키워드를 사용해서 직접 구현하려는 메소드 이외의 다른 메소드들을 컴파일러가 자동으로 생성해 줍니다.

코틀린으로 위임을 쉽게 사용할 수 있기 때문에 과다한 상속으로 인해 발생하는 사이드 이펙트를 줄일 수 있을 것입니다.



*개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.


댓글

  1. 헷갈렸던 개념인데 잘 정리해주셔서 감사합니다 :)

    답글삭제
  2. 정리 감사합니다 :)

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

코틀린 (Kotlin) 인터페이스 정리

RecyclerView 에서 notifyItemChanged()의 payload 이해하기