본문 바로가기
공부/Kotlin

4장. 클래스, 객체, 인터페이스

by JERO__ 2022. 6. 7.

클래스 계층 정의

코틀린 인터페이스

자바와 다른점

  • 디폴트 구현 메서드가 가능하다
  • 추상 프로퍼티 선언을 넣을 수 있다
  • 구체적 타입 지정 문법이 다르다
    • 자바 : Clickable.super.showOff()
    • 코틀린 : super<Clickable>.showOff()
interface Clickable {
    fun click()
    fun showOff() = println("i'm clickable")
}
interface Focusable {
    fun showOff() = println("i'm focusable")
}
class Button : Clickable, Focusable{
    override fun click() = println("i was clicked")
    override fun showOff() {                 // override를 필수로 해주어야 함
        super<Clickable>.showOff()
        super<Focusable>.showOff()
    }
}
  • 상속은 :
  • override 필수 사용

인퍼페이스의 프로퍼티

interface User {
    val nickname: String              // 추상 프로퍼티
}

class PrivateUser(override val nickname:String) : User

class SubscribingUser(val email: String) : User {
    override val nickname: String
        get() = email.substringBefore('@')        // 필드가 값을 저장하지 않고 계산해 반환
}

open, final, abstract 변경자 : 기본적으로 final

open

  • 클래스의 상속을 허용
  • 메서드와 프로퍼티의 오버라이드를 허용
open class RichButton : Clickable {
    fun disable() {}              // final
    open fun animate(){}          // 열려있다. 하위클래스에서 오버라이드 가능
    override fun click() {}       // 상위 클래스 오버라이드 / 열려있다(default)
    final override fun click() {} // 상위 클래스 오버라이드 / final
}

abstract

  • 인스턴스화 할 수 없다.
  • 항상 open 이다. → open을 명시할 필요가 없다.
abstract class Animated {
    abstract fun anmiate()       // 반드시 하위클래스에서 오버라이드
    open fun stopAnimating() {}  // 명시할 필요 없다. default open
    fun animateTwice(){}         // open
}

가시성 변경자

종류

  • public : 기본 가시성
  • internal : 같은 모듈 안
    • 모듈 : 한 번에 한꺼번에 컴파일되는 코틀린 파일들
  • protected : 하위 클래스
  • private : 같은 클래스

public 함수가 그보다 더 낮은(internal…) 가시성 타입을 참조하지 못한다. 같거나 상위 타입은 가능하다.

내부 클래스에서 바깥쪽 클래스 참조에 접근하기

class Outer {
    inner class Inner {
        fun getOuterReference(): Outer = this@Outer
    }
}
  • this@Outer 로 접근 가능하다.

sealed class

  • when을 사용할 경우 else를 제외하고 사용할 수 있다.
sealed class Expr {
    class Num(val value: Int) : Expr()
    class Sum(val left: Expr, val right: Expr) : Expr()
}
fun eval(e: Expr): Int =
    when(e) {
        is Expr.Num -> e.value
        is Expr.Sum -> eval(e.left) + eval(e.right)
				// else 가 필요없다.
    }

생성자

클래스 초기화

class User(_nickname: String) {
    val nickname: String

    init {
        nickname = _nickname
    }
}
  • 초기화블록은 주 생성자와 함께 ㅡ사용된다.
  • 모든 생성자 파라미터에 디폴트 값을 지정하면, 컴파일러가 자동으로 파라미터가 없는 생성자를 만든다.

비공개 생성자

class User private constructor() {}

부 생성자

인자에 대한 디폴트 값을 제공하기 위한 부 생성자를 여럿 만들지 말자.

class View {            // 주 생성자는 선언하지 않은 것임
    constructor(ctx: String) {}
    constructor(ctx: String, id: Int) {}
}

부 생성자가 필요한 주된 이유?

  • 자바 상호운용성
  • 파라미터 목록이 다른 생성방법이 여럿 존재하는 경우

게터와 세터의 필드 접근

field : 변경 가능 프로퍼티의 게터와 세터 중 한쪽만 직접 정의해도 된다.

접근자의 가시성 변경

var counter: Int = 0
	private set                 // 클래스 밖에서 이 프로퍼티의 값 변경이 불가능

데이터 클래스 data

  • toString()
  • equals()
  • hashCode()

데이터 클래스와 불변성

데이터클래스를 불변 클래스로 만들라고 권장한다. 이를 위해 메서드는 객체를 복사(copy) 하여 객체를 메모리상에서 직접 바꾸는 대신 복사본을 만들어 수행한다.

클래스 위임 : by

상속의 문제

  • 하위 클래스가 상윜 클래스의 메서드 중 일부를 오버라이드한 상태
  • 하위클래스는 상위클래스의 세부 구현사항에 의존
  • 상위클래스의 변경이 발생하면 하위 클래스가 깨져버림

이를 위해 기본적으로 final이며 open변경자로 상속한다.

하지만, 상속을 허용하지 않는 클래스에 새로운 동작을 추가할 때가 있다.

데코레이터 패턴을 사용한다.

데코레이터 패턴

상속을 허용하지 않는 클래스 대신 사용할 수 있는 새로운 클래스를 만들되

  • 기존 클래스와 같은 인터페이스를 제공하게 만들고
  • 기존 클래스를 내부 필드로 유지하는 것이다.
class DelegatingCollection<T>(innerList: Collection<T> = ArrayList<T>())
 : Collection<T> by innerList{}

object

  • 객체 선언 : 싱글턴 정의
  • 동반 객체 : 인스턴스 메서드는 아니지만 클래스와 관련있는 메서드와 팩토리 메서드를 담을 때
  • 객체 식 : 무명 내부 클래스

1. 객체 선언

object ExampleComparator : Comparable<String> {
    override fun compareTo(other: String): Int {
        return this.compareTo(other)
    }
}
class Person(val name: String){
    object ExampleComparator : Comparable<String> {
        override fun compareTo(other: String): Int {
            return this.compareTo(other)
        }
    }
}
  • 인스턴스는 단 하나 뿐

2. 동반 객체 companion

  • 동반 객체가 정의된 클래스 이름 사용
  • 해당 클래스의 모든 private 멤버에 접근할 수 있다.
class User private constructor(val name: String) {        // 주 생성자 비공개
    companion object {                                    // 이름도 붙일 수 있다
        fun newUser(email: String) = User(email.substringBefore('@'))
    }
}

 

kotlin in action 책을 통한 공부입니다.

 

댓글