ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4장 - 클래스, 객체, 인터페이스
    Kotlin/코틀린인액션요약 2022. 9. 12. 00:01
    728x90

    4장에서 다루는 내용

    - 클래스와 인터페이스

    - 뻔하지 않은 생성자와 프로퍼티

    - 데이터 클래스

    - 클래스 위임

    - object 키워드 사용

     

    클래스 계층 정의

    코틀린에 새로 도입한 sealed 변경자는 클래스 상속을 제한합니다.

     

    코틀린 인터페이스

    코틀린의 인터페이스는 추상 메서드, 구현이 있는 메서드도 정의할 수 있습니다.

    즉, 자바 8의 디폴트 메서드와 비슷합니다.

     

    인터페이스

    interface Clickable {
    	fun click() //추상 메서드
    	fun showOff() = println("I'm clickable!") //디폴트 구현이 있는 메서드
    }

    자바와 달리 default를 붙일 필요가 없습니다.

     

    단순한 인터페이스 구현

    class Button : Clickable{
    	override fun click() = println("I was clicked")    
    }
    
    Button.click()
    //출력 : I was clicked

    자바에서는 implements, extends 키워드를 사용하지만 코틀린은 클래스 이름 뒤에 콜론(:)을 붙이고 인터페이스와 클래스 이름을 적는 것으로 처리합니다.

     

    두 인터페이스의 디폴트 메서드가 중복되는 경우

    class Button: Clickable, Focusable{
    	override fun click() = println("I was clicked")
        override fun showOff(){
        	super<Clickable>.showOff()
            super<Focusable>.showOff()
        }
    }

     

    기본적으로 final

    자바에서는 상속을 명시적으로 금지하지 않으면 모든 클래스를 다른 클래스가 상속할 수 있습니다.

    하지만 상속은 하위 클래스의 오버라이드를 통해 예상치 못한 동작을 유발할 수 있습니다.

    따라서 기본적으로 상속을 금지시킵니다.

     

    어떤 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 합니다.

    open class RichButton : Clickable {
    	fun disable() {} //final 오버라이드 불가능
    	open fun animate() {} //open 오버라이드 가능
    	override fun click() {} //open되어야 오버라이드 가능하기 때문에 open 상태
    	final override fun clickMakeFinal() {} //open된 것을 final으로 변환
    }

     

    추상 클래스 정의하기

    abstract class Animated{
    	abstract fun animate()	//추상 클래스
    	open fun stopAnimating(){}	//추상 클래스에 속해도 기본적으로 final
    	fun animateTwice(){}    
    }

     

    final 오버라이드 할 수 없음 - (기본)

    open 오버라이드 가능 - (open 명시)

    abstract 반드시 오버라이드 해야 함 - (추상 클래스 멤버에만 붙이기 가능)

    override 상위 클래스나 인스턴스 멤버 재구현

     

    가시성 변경자

    public, protected, private처럼 클래스 외부 접근을 제어합니다.

    코틀린은 기본으로 모두 public입니다.

     

    코틀린에서는 internal이라는 새로운 가시성 변경자를 도입했습니다.

    internal은 모듈 내부에서만 볼 수 있다는 뜻으로 모듈은 한 번에 컴파일되는 코틀린 파일을 의미합니다.

     

    public - (기본)

    internal - (같은 모듈 안에서만 볼 수 있음)

    protected - (하위 클래스 안에서만 볼 수 있음)

    private - (같은 클래스 안에서만 볼 수 있음)

     

    내부 클래스

    클래스 안에 다른 클래스를 선언할 수 있습니다.

    자바와의 차이는 중첩 클래스는 명시적으로 요청하지 않는 한 바깥쪽 클래스 인스턴스에 대한 접근 권한이 없습니다.

     

    자바의 중첩 클래스 static class A

    자바의 내부 클래스 class A

     

    코틀린의 중첩 클래스 class A

    코틀린의 내부 클래스 inner class A

     

    봉인된 클래스

    sealed 변경자를 통해 상위 클래스를 상속한 하위 클래스 정의를 제한할 수 있습니다.

    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)
        }

    when 식에서 sealed 클래스의 모든 하위 클래스를 처리한다면 디폴트 분기(else)가 필요 없습니다.

    sealed class는 자동으로 open입니다.

     

    또한 sealed class에 새로운 클래스가 추가되면 컴파일에 에러가 발생하여 when 식을 고쳐야 한다는 사실을 쉽게 알 수 있습니다.

     

     

    생성자와 프로퍼티

    코틀린은 생성자를 주생성자와 부 생성자로 구분합니다.

     

    또한 초기화 블록(init)을 사용하여 초기화 로직을 추가할 수 있습니다.

     

    클래스 이름 뒤에 오는 주 생성자

    class User(val nickname: String)

     

    초기화 블록 사용하기

    class User constructor(_nickname: String){
    	val nickname = String
    	init{
    		nickname = _nickname
    	}
    }

    constructor 키워드는 주 생성자나 부 생성자를 정의할 때 사용합니다.

    _는 자바에서 사용하는 것처럼 this.nickname = nickname으로 대체해서 사용해도 됩니다.

     

    간소화시키기

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

     

    생성자 캡슐화

    class Secretive private constructor() {}

    private constructor를 사용하여 외부에서 호출할 수 없습니다.

     

    부 생성자

    대부분 주 생성자를 통해 해결할 수 있습니다.

     

    보통 인스턴스를 여러 가지 방법으로 초기화할 수 있어야 하는 경우에 사용됩니다.

    또한 자바와의 상호운영성을 위해 사용됩니다.

     

    인터페이스와 프로퍼티

    인터페이스에 추상 프로퍼티를 넣을 수 있습니다.

    interface User {
    	val nickname : String
    }

    이는 User 인터페이스가 구현하는 클래스가 nickname의 값을 얻을 수 있는 방법을 제공해야 한다는 뜻입니다.

     

     

    인터페이스의 프로퍼티 구현

    interface Member{
        val nickname : String
    }
    
    class PrivateMember(override val nickname : String) : Member
    
    class SubscribingMember(val email : String) : Member{
        override val nickname: String
            get() = email.substringBefore('@')
    }
    
    class FacebookUser (val accountId : Int) : Member{
        override val nickname = getFacebookName(accountId)
    }
    
    fun main() {
        println(PrivateMember("test@kotlinlang.org").nickname)
        //출력 : test@kotlinlang.org
        
        println(SubscribingMember("test@kotlinlang.org").nickname)
        //출력 : test
        
    }

    SubscribingMember와 FacebookUser의 차이가 존재합니다.

    SubscribingUser의 nickname은 매번 호출될 때마다 subStringBefore를 호출해 계산하는 커스텀 게터를 활용합니다.

    FaceBookUser는 객체 초기화 시 한번 호출하여 저장했다가 그 값을 불러오는 방식을 활용합니다.

     

    데이터 클래스

    코틀린 컴파일러는 equals, hashCode, toString 등을 보이지 않는 곳에서 생성해줍니다.

    data class Client(val name: String, val postalCode: Int)

    클래스 앞에 data 키워드를 붙이면 끝입니다.

     

    자바와의 조금 다른 점은 copy()를 통해 불변 객체를 복사할 수 있는 수단을 제공합니다.

     

    클래스 위임 : by 키워드

    대규모 객체지향 시스템을 설계하면 상속에 의해서 하위 클래스가 상위 클래스의 구현을 깨버리면서 코드가 정상적으로 작동하지 않는 문제가 발생할 수 있습니다.

     

    코틀린은 이런 문제를 인식하고 기본적으로 클래스를 final으로 취급합니다.

     

    일반적으로 상속을 허용하지 않는 클래스에 동작을 추가하기 위해서는 데코레이터 패턴을 통해 해결할 수 있습니다.

     

    하지만 단점으로 준비 코드가 많이 필요합니다.

     

    코틀린은 인터페이스를 구현할 때 by 키워드를 통해 그 인터페이스에 대한 구현을 다른 객체에 위임 중이라는 사실을 명시할 수 있습니다.

     

    object 키워드

    object키워드를 통해 클래스를 정의하면서 동시에 인스턴스를 생성합니다.

     

    이를 통해 싱글턴을 쉽게 만들어낼 수 있습니다.

     

    companion 키워드

    companion을 사용하면 모든 private 멤버에 접근할 수 있습니다.

    따라서 companion을 사용하면 팩토리 패턴을 구현하기 좋습니다.

     

    companion + object 활용

    data class Book private constructor(val id :Int , val name : String){
    
        companion object {
            fun create() = Book(0, "animal fram")
        }
    }
    
    fun main() {
        val myBook = Book.create()
        println(myBook)
    }

    private constructor에 접근할 수 있습니다.

     

     

    'Kotlin > 코틀린인액션요약' 카테고리의 다른 글

    6장 - 코틀린 타입 시스템  (0) 2022.09.16
    5장 - 람다로 프로그래밍  (0) 2022.09.13
    3장 - 함수 정의와 호출  (1) 2022.09.11
    2장 - 코틀린 기초  (1) 2022.09.10
    1장 - 코틀린소개  (0) 2022.09.08

    댓글

Designed by Tistory.