독서관련/Kotlin in Action

CH04 코틀린 클래스 객체 인터페이스

ColinKang 2021. 1. 3. 19:18
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석 역, 『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

클래스와 인터페이스

 

1. 인터페이스에 프로퍼티 선언이 들어갈 수 있다.

2. sealed는 클래스 상속을 제한한다. ( when으로 분기 타는데 상속 받으면 else 에서 처리 될 수도 있음, 이를 막자)

3. 기본 선언은 public final 임

4. 인터페이스에 구현이 있는 메소드 (java 8의 디폴트 메서드와 비슷)

5. 클래스 이름 뒤에 : 인터페이스 혹은 클래스 ( extends와 implements가 별도 없다.)

6. override 변경자 반드시 사용

7. 디폴트 메서드 default 안 붙여도 됨

8. open final abstract 변경자 (  modifier ) 

9. 상속을 허용 하려면 open을 붙여야 한다.

10. visibility modifier : public , private, protected, internal (자바의 기본인 패키지 전용은 없음), internal : 한꺼번에 컴파일 되는 코틀린 파일

11. 최상위 선언 ( 클래스 함수 프로퍼티 )에 private 가능

12. protected 오직 상속 클래스에서만 접근 ( 자바는 같은 패키지 안에서 접근 가능)

13. 코틀린 nested class : class A, inner class : inter class A ( 자바 nested class : static class A, inner class : class A (클래스안에)

 

* fragile base class : 하위 클래스가 상속할 경우 상위 클래스의 정확한 규칙을 이해 못하고 구현 할 경우 문제가 발생할 수 있음. effective java에서는 확실하지 않으면 상속 못하게 하던지 설명을 다 넣던지. 

 

interface Clickable {
	fun click()
    fun showOff() = println("clickable.");
}

interface Focusable {
	fun setFocus(value: Boolean) = println("focus { if (value) "on" else "off"}.")
    fun showOff() = println("focusable.");
}

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

open class RichButton: Clickable {
	fun diable() {}
    open fun animate() {}
    final override fun click() {}
}

abstract class Animated {
	abstract fun animate()
}

class Outer {
	inner class Inner {
    	fun getOuterReference(): Outer = this@Outer
    }
}

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 -> e.left + e.right
        // else 가 필요 없음 sealed 키워드 
        // 추후에 Expr에 추가되면 컴파일 오류나고 여기에도 추가해야됨. 
    }

 

생성자 관련

 

14. 기반 클래스 이름 위에는 꼭 빈 괄호가 들어간다.

15. 싱글턴 클래스 > 비공개 생성자 private constructor

16. 부 생성자가 필요한 이유는 자바 상호 운영성

 

class User(val nickname: String)

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

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

class User(val nickname: String, val isSubscribed: Boolean = true)

open class Button // 인자가 없는 디폴트 생성자가 만들어진다.

class RadioButton: Button() // Button 클래스의 생성자를 호출

class Secretive private constructor() {}


open class View {
	constructor(ctx: Context) {
    }
    
    constructor(ctx: Context, attr: AttributeSet) {
    }
}

class MyButton: View {
	constructor(ctx: Context) : super(ctx) {
    }
    
    constructor(ctx: Context, attr : AttributeSet) : super(ctx, attr) {
    }
}

class MyButton2: View {
	constructor(ctx: Context) : this(ctx, MYSTYLE) {
    }
    constructor(ctx: Context, attr : AttributeSet) : super(ctx, attr) {
    }
}

 

인터페이스에 선언된 프로퍼티

 

interface User {)
	val nickname : String
}

class PrivateUser(override val nickname: String) : User

class SubscribeUser(val email: String) : User {
	override val nickname: String 
        // 이 값 불리때 마다 실행
    	get() = email.substringBefore('@')
}

class FacebookUser(val accountId: Int) : User {
    // 방식 초기화시 한번 실행
	override val nickname = getFacebookName(accountId)
    // getFackbookName은 다른 곳에 정의 된 것으로 가정
}

 

어떤 값을 저장하되 그 값을 변경하거나 읽을 때 정해진 로직을 실행하는 프로퍼티

field라는 특별한 식별자를 통해서 뒷받침할 필드에 접근 가능

게터에서는 field 값을 읽을 수만 있고, 세터에서는 field 값을 읽고 쓰기 가능

 

class User(val name: String) {
	var address: String = "unknown"
    set(value: String) {
    	println("address changed ${field} -> ${value}")
        field = value
    }
}


class LengthCounter {
	var counter: Int = 0
    private set
    fun addWord(word: String) {
    	counter += word.length
    }
 }