본문 바로가기

Kotlin

Kotlin In Action - 4. 클래스, 객체, 인터페이스

  • Kotlin의 기본 선언은 final, public
  • 중첩 클래스는 내부 클래스가 아니다

4.1 클래스 계층 정의

  1. 코틀린 인터페이스
    1. 추상메서드 뿐 아니라 디폴트 구현이 있는 메소드도 정의 가능
      1. public static final class로 만든 후 내부 메서드로 구현
    2. override 변경자 필수 사용
    3. 같은 이름의 메소스 구현시 오버라이딩 메소드를 직접 제공하지 않으면 에러 발생
    4. super<타입> 으로 구현
    5. 자바에서는 코틀린의 디폴트 메서드 구현에 의존할 수 없다.
      1. 일반 인터페이스와 디폴트 메서드 구현이 정적 메서드로 들어있는 클래스를 조합해 구현
    6. 멤버가 항상 열려있다. final, open, abstract 사용 안함
  2. open, final, abstarct 변경자
    1. 취약한 기반 클래스
      1. 기반이 되는 클래스 변경시 하위 클래스의 동작이 예기치 않게 바뀌는 문제
      2. 해결 방안: 문서를 잘 갖출 수 없다면, 상속을 금하라
    2. 상속을 허용하려면 open 변경자 사용
    3. 하위 클래스에서 override한 메서드의 상속을 막으려면 final 변경자 사용
      1. 장점으로 스마트 캐스트가 가능하다. / 커스텀 접근자를 정의함으로써 요구 사항을 깨는 것을 방지
  3. 가시성 변경자
    1. 코틀린은 패키지를 네임스페이스 관리용으로만 사용
    2. 모듈: 한꺼번에 컴파일되는 코틀린 파일
    3. 가시성 변경자 종류
      1. public: 기본 가시성 / 모든 곳
      2. internal: 같은 모듈 / 같은 모듈
        1. 자바에서는 public 처리
        2. 멤버의 이름을 kotlin$KotlinInAction()과 같이 바꿈
      3. protected: 하위 클래스 안에서만
        1. 자바는 같은 패키지 안에서 접근 가능하지만, 코틀린은 하위 클래스만
      4. private: 같은 클래스 안에서만 / 같은 파일 안에서만
        1. 코틀린 private - 자바 package-private
    4. 메서드 시그니처나 타입 들은 기반타입의 가시성이 본인과 같거나 높아야 한다.
      1. 모든 타입에 접근할 수 있도록 보장하기 위함
    5. 외부 클래스가 내부 클래스나 중첩된 클래스의 priavte에 접근 할 수 없다.
  4. 내부 클래스와 중첩된 클래스 : 기본적으로 중첩 클래스
    1. 코틀린에서 아무것도 붙지 않으면 자바의 static 중첩 클래스와 같다.
    2. 내부 클래스로 변경하여 바깥참조를 포함하고 싶으면 inner 변경자를 붙여야 한다.
  5. 봉인된 클래스: 클래스 계층 정의 시 계층 확장 제한
    1. 상위 클래스에 sealed 변경자를 붙이면 그 상위 클래스를 상속한 하위 클래스 정의를 제한
      1. when의 분기에 활용하는데 좋다.

4.2 뻔하지 않은 생성자와 프로퍼티를 갖는 클래스 선언

  • 코틀린은 주 생성자와 부 생성자로 구분한다.
  •  초기화 블록을 통해 초기화 로직을 추가할 수 있다.
  1. 클래스 초기화: 주 생성자와 초기화 블록
    1. 클래스 명 뒤에 오는 괄호로 둘러쌓인 코드를 주 생성자
      1. 생성자 파라미터 지정 및 프로퍼티를 정의
    2. constructor :주, 부 생성자 정의
      1. 생략 조건: 주 생성자 앞에 별도의 애노테이션이나 가시성 변경자가 없으면 생략 가능
    3. init: 초기화 블럭
      1. 프로퍼티 선언에 포함 시킬수 있는 경우
    4. 디폴트 값 정의 가능
    5. new 키워드 없이 인스턴스 생성
    6. 모든 생성자 파라미터에 디폴트 값을 지정하면 컴파일러가 파라미터가 없는 생성자를 만든다.
      1. DI의 경우 파라미터가 없는 생성자를 통해 객체를 생성해야만 라이브러리가 사용이 가능한 경우가 있는데 그 경우에 좋다.
    7. 하위 클래스는 기반 클래스의 생성자를 호출해야 하므로 이름 뒤에 빈 괄호가 들어가는 것
    8. 클래스 외부에서 인스턴스화를 막고 싶다면 constructor() 앞에 private를 붙이면 된다.
      1. 유틸리티 함수를 담는 클래스에 사용
  2. 부 생성자: 상위 클래스를 다른 방식으로 초기화
    1. 클래스의 확장하기 위한 방안으로 다양한 생성자를 지원해야하는 경우
    2. contructor를 이용해 부 생성자를 만든다.
    3. 클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화 하거나, 다른 생성자에게 생성을 위임해아한다.
    4. 부 생성자가 필요한 이유: 상호 운용성
  3. 인터페이스에 선언된 프로퍼티 구현
    1. getter,settter가 있는 프로퍼티 선언 가능
  4. 게터와 세터에서 뒷받침하는 필드에 접근
    1. 변경 가능 프로퍼티에서 한쪽만 직접 정의해도 된다.
  5. 접근자의 가시성 변경
    1. 접근자의 가시성은 프로퍼티와 같다.
    2. public으로 선언해서 get은 가능하지만, set을 private으로 놓아서 변경을 fun addWord로만 가능하게 한다.

4.3 컴파일러가 생성한 메소드 : 데이터 클래스와 클래스 위임

  • equals, hashCode, toString을 컴파일러가 안보이는 곳에서 생성
  1. 모든 클래스가 정의해야 하는 메소드
    1. 코틀린에서는 == 는 eqauls를 호출(동등성 비교)  ===가 참조를 비교
    2. eqauls override시 hashcode도 override 해줘야 한다.
  2. 데이터 클래스 : 모든 클래스가 정의해야 하는 메소드 자동 생성
    1. data class라고 선언
      1. 내부적으로 * 31 해가면서 hashCode 구현 / 31인 이유 : 소수이면서 shift연산 - i 한 것과 같아서 최적화 가능
    2. val로 만들어서 불변클래스로 만들것을 권장 / 동기화 문제 해결
    3. copy: 객체를 복사하면서 일부 프로퍼티 변경 가능한 메소드
  3. 클래스 위임: by 키워드 사용
    1. 상위 클래스의 메소드 변경시 하위 클래스에서 갖고 있던 가정이 깨져 작동하지 않는 경우를 해소하기 위한 목적
    2. 상속을 허용하지 않는 클래스에 새로운 동작을 추가해야할 때 일반적인 방법이 데코레이터 패턴

4.4 object 키워드 : 클래스 선언과 인스턴스 생성

  • 클래스를 정의하면서 인스턴스를 생성
  • companion object: 클래스와 관련있는 메소드와 팩토리 메소드를 담을 때
  • 자바의 무명 내부 클래스 대신
  1. 객체 선언: 싱글턴을 쉽게 만들기
    1. 객체 선언 기능을 통해 싱글턴 패턴을 지원
      1. 객체 선언: 클래스 선언과 단일 인스턴스 선언을 합친 선언
      2. 자바에서는 static INSTANCE 변수를 만들어서 static 블럭에서 초기화
    2. 프로퍼티, 메소드, 초기화 블록 등이 들어갈 수 있다.
    3. 생성자는 객체 선언에 쓸 수 없다.
    4. 객체를 인자로 받는곳에서 사용가능하다.
    5. 대규모에서 싱글턴은 객체 생성을 제어할 방법이 없고 생성자 파라미터를 지정할 수 없어서 적합하지 않다.
    6. 비교하는 클래스를 내부에 정의하는 것이 바람직하다.
  2. 동반 객체: 팩토리 메소드와 정적 멤버가 들어갈 장소
    1. 자바의 static 키워드를 지원하지 않는다. 대신 최상위 함수와 객체 선언을 활용
    2. 동반 객체는 자신을 둘러싼 클래스의 private 멤버에 접근 가능  / private 생성자를 호출하기 좋은 위치
      1. 자바에서는 static
    3. 동반 객체는 팩토리 패턴을 구현하기 가장 적합한 위치
    4. 생성할 필요가 없는 객체를 생성하지 않을 수 있다.
  3. 동반 객체를 일반 객체처럼 사용
    1. 동반 객체는 클래스 안에 정의된 일반 객체다.
      1. 이름을 붙이거나, 인터페이스 상속, 확창 함수와 프로퍼티 정의 가능
      2. 자바에서 사용하기 위해 코틀린 클래스의 멤버를 정적인 멤버로 만들려면 @JvmStatic 어노테이션 사용
    2. 클래스.companion.확장함수를 이용해 외부에서 정의할 수 있다.
      1. 대신 비어있는 companion object가 있어야 한다.
  4. 객체 식: 무명 내부 클래스를 다른 방식으로 작성
    1. 자바에서 무명 내부 클래스와 같이 이름이 없다.
      1. 이름이 필요하면 변수에 대입
    2. 무명 객체 안에서 로컬 변수의 사용이 가능하다.
    3. 무명 객체는 싱글턴이 아니라 쓰일 때마다 새로운 인스턴스 생성
    4. 메소드가 하나뿐인 인터페이스를 구현해야 한다면, SAM 인터페이스와 람다를 사용해서 구현