Kotlin ‐ 클래스[Effective Kotlin Item 9] - thought-corner/Backend-PlayGround GitHub Wiki

클래스

  • 클래스 본문에서 정의된 모든 요소는 멤버라고 부르며, 클래스 본문에서 정의된 함수는 멤버 함수(member function)라고 한다.
  • 클래스와 연관된 함수는 메서드이기 때문에 모든 멤버 함수는 메서드이다.
  • 자바는 기본적으로 상속이 가능하고 final을 붙여야 상속이 막히지만, 코틀린은 정반대이다. 기본적으로 모든 클래스와 메서드는 상속/오버라이딩이 불가능한 final 상태이다.
  • 상속을 허용하려면 반드시 클래스와 메서드 안에 open 키워드를 명시해야 한다.

프로퍼티(Property)

  • 클래스 본문에서 변수를 정의할 수 있다.
  • 클래스 안에서 정의된 변수를 필드(field)라고 부른다.
    • 게터(Getter) : 필드의 현재 값을 얻는 함수
    • 세터(Setter) : 필드에 새로운 값을 설정하는 함수
  • 프로퍼티는 캡슐화가 자동으로 이루어지는 클래스 변수다.
  • 즉, 내부적으로 알아서 Getter/Setter를 사용한다.
  • 코틀린에서는 클래스 내 모든 변수가 필드가 아닌 프로퍼티로 인식된다.
  • 코틀린/JVM에서는 모든 프로퍼티 각각에 대해 (읽기만 가능한) val 변수에는 Getter가, (읽기/쓰기가 모두 가능한) var 변수에는 Getter/Setter가 만들어진다.

생성자(Constructor)

  • 객체는 보통 특정한 값으로 초기화해 생성한다. 이럴 때, 생성자를 사용한다.
  • 생성자를 따로 정의하지 않으면 기본적으로 매개변수가 없는 생성자가 만들어진다.
  • 생성자는 프로퍼티 초기값을 설정하는데 주로 사용한다.
  • 주 생성자는 단 하나만 존재하고 다른 생성자는 모두 부 생성자에 해당되며, this 키워드를 이용해 주 생성자를 호출해야 한다.
  • 초기화 블록(init) : 주 생성자 안에 코드를 작성할 수 없으므로 객체 생성 시 검증 로직이나 초기화 로직이 필요할 때는 init 블록을 사용한다.
// constructor 키워드는 생략해도 상관없다.
class User(var name: String, var username: String) {
    
    // 객체가 생성될 때 실행되는 초기화 블록
    init {
        require(name.isNotBlank()) { "이름은 비어있을 수 없습니다." }
    }
}

내부 클래스

  • 코틀린에서는 클래스 내부에 클래스를 정의할 수 있다.
  • 기본적으로 정적이기 때문에 외부 클래스로의 직접적인 연결 관계가 없다.
  • 따라서 외부 클래스의 인스턴스가 없어도 내부 클래스 객체를 곧바로 만들 수 있다.
class Puppy(val name: String) {
    class InnerPuppy {
        fun think() {
            println("Test")
        }
    }
}
  • 내부 클래스가 외부 클래스를 참조하려면 inner 제어자를 사용해 내부 클래스임을 명시해야 한다.
  • 단, 이렇게 하면 내부 클래스 객체를 만들 때, 외부 클래스의 인스턴스가 반드시 필요하다.
  • 자바에서는 기본 내부 클래스가 외부 클래스의 참조를 암묵적으로 들고 있어서 외부 클래스가 사용되지 않아도 GC가 메모리를 회수하지 못해 메모리 누수가 빈번하게 발생했다. 그러나 코틀린은 이를 원천 차단하기 위해 기본값을 정적 중첩 클래스로 만들고 꼭 필요한 경우에만 inner 키워드를 명시하도록 강제했다.
class Puppy(val name: String) {
    inner class InnerPuppy {
        fun think() {
            println("Test")
        }
    }
}