쾌락코딩

코틀린 프로퍼티(kotlin property)

|

코틀린은 프로퍼티를 언어 기본 기능으로 제공한다. 프로퍼티란 무엇일까? 프로퍼티란 필드와 접근자를 통칭하는 것이다. 일반적인 자바빈 클래스인 Person을 보면서 정확히 알아보자.

public class Person {
    private final String name;
    private boolean isMarried;

    public Person(String name, boolean isMarried) {
        this.name = name;
        this.isMarried = isMarried;
    }

    public String getName() {
        return this.name;
    }

    public void setIsMarried(boolean isMarried) {
        this.isMarried = isMarried;
    }

    public boolean getIsMarried() {
        return this.isMarried;
    }
}

자바에서는 데이터를 필드(field)에 저장한다. nameisMarried라는 데이터를 Person클래스의 필드에 저장한 것이다. 한편 각 데이터마다 적용되는 getter와 setter를 접근자라 부른다. 이 접근자를 통해서 가시성이 private인 데이터들에 접근할 수 있다.

위의 자바 코드에서 Person클래스 필드에 들어가는 데이터들이 점점 증가한다면 getter와 setter같은 보일러플레이트 코드가 지저분하게 많아진다. 코틀린에서는 위의 Person클래스를 간단하게 정의할 수 있다.

class Person(val name: String, var isMarried: Boolean)

이 한줄의 코틀린 코드는 21줄의 자바 코드와 완전히 동일한 코드다.

자세히보면 자바코드에서 setter를 제공하지 않는 nameval로 선언하였고, getter와 setter모두 제공하는(조회화 변경을 모두 허용하는) isMarried는 var로 선언했다. 익히 알고있듯이 val은 불변, var은 가변 데이터를 선언할 때 사용한다. 이와 같은 맥락으로 val로 선언한 name은 setter가 생성되지 않는다. 이 부분을 코드로 보자면 아래와 같다.

class Person {
    val name: Int
        get() {
            return this.age
        }

    var isMarried: Boolean
        get() {
            return this.isMarried
        }
        set(isMarried: Boolean) {
            this.isMarried = isMarried
        }
}

val로 선언한 name의 경우 setter가 없다. setter를 만들고 싶어도 val은 불변이기에 만들 수 없다(억지로 만드려고 하면 컴파일에러가 뜬다). 참고로 위에서 get()set()을 정해준 것은 커스텀 접근자이다. 기본적으로는 코틀린 클래스를 만들 때 생성자에 넣어준 데이터들에 대하여 get()과 set()이 숨겨져 있으나, 위의 코드처럼 명시적으로 적어줄 수 있다. 그말은 getter와 setter를 커스텀 할 수도 있다는 뜻이다.

생성자에 val, var의 유무 차이

class Person(val name: String) // 1

class Person(name: String) //2

이 둘의 차이는 무엇일까? 먼저 생성자에 val(또는 var)이 있는 경우 멤버변수로 변환된다. 즉 class Person(val name: String)의 경우 아래 자바 코드로 변환된다.

public final class Person {
   @NotNull
   private final String name;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public Person(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
   }
}

반면 class Person(name: String)의 경우 아래의 자바 코드와 같다.

public final class Person {
   public Person(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
   }
}

코틀린 클래스 생성자에 val이나 var이 없는 경우에는 주 생성자의 파라미터들은 딱 생성자(init {...}) 또는 프로퍼티를 초기화 하는 식에서만 사용 가능하다. 따라서 클래스의 생성자 외 다른 메서드에서는 사용할 수 없다(프로퍼티가 되지 못했기 때문).

class Person(name: String) {
    init {
        println(name)
    }
}

위 코드는 아래의 자바 코드로 변환된다.

public final class Person {
   public Person(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      System.out.println(name);
   }
}

주의할 점

디컴파일한 자바 코드에서 필드가 private이라고 하여 코틀린의 프로퍼티도 private은 아니다. 이게 무슨말인가 싶겠지만, 필드와 프로퍼티를 다르게 인식할 줄 알아야 한다. 자바는 기본적으로 필드로 다루고, 코틀린은 프로퍼티(필드 + 접근자)를 기본으로 다루는 언어다.

class Person(var name: String)

위의 코드는 아래의 자바코드가 된다.

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public Void setName(String value) {
        this.name = value;
    }

    public String getName() {
        return this.name;
    }

}

자바 필드인 name자체만 보면 private 키워드가 붙어있으므로 private이 맞지만, 프로퍼티 전체를 보면 다르다. 필드는 private이지만 getter와 setter로 접근이 모두 가능하기 때문에 프로퍼티는 private하다고 볼 수 없다. 위의 코드에서 name 프로퍼티가 private이기 위해서는 아래와 같은 코틀린 코드가 필요하다.

class Person(private var name: String)

name 앞에 private이 붙었다. private이 붙지 않은 상태였어도 디컴파일한 자바 코드의 필드에는 private이 붙지만, 코틀린은 기본적으로 필드가 아닌 프로퍼티를 다루기 때문에 프로퍼티 전체가 private이 된다. 디컴파일된 자바 코드는 아래와 같다.

public final class Property {
   private String name;

   public Property(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
   }
}

getter와 setter가 없어서 프로퍼티가 private이라고 볼 수 있다.

핵심

  • 자바는 필드를 기본으로, 코틀린은 프로퍼티를 기본으로 다룬다.
  • 디컴파일된 자바 코드의 필드가 private이라고해서 kotlin 프로퍼티가 private인 것은 아니다.

Comments