Files
2024-06-21 15:01:25 +09:00

6.2 KiB

객체

다음과 같이 클래스를 선언합니다.

class Shape {
    // ...
}

바디가 없는 클래스도 있습니다.

class Empty

생성자

기본 생성자

기본 생성자는 constructor를 사용해서 클래스 헤더 부분에 정의합니다.

class Person constructor(name: String) {
    // ...
}

기본 생성자에 별다른 어노테이션이나 접근한정자를 명시할 필요가 없는 경우에는 constructor 키워드를 생략할 수 있습니다.

class Person (name: String) {
    // ...
} 

class Person public constructor(name: String) {
    // ...
}

기본 생성자의 경우, 별도의 초기화 로직을 정의할 수 없는 대신, 초기화 블록을 사용할 수 있습니다. 초기화 블록은 init을 사용해서 정의하며, 여러 개의 초기화 블록이 있을 경우, 순서대로 실행됩니다. 기본 생성자에 선언된 매개 변수는 초기화 블록에서 사용할 수 있습니다.

class Person (name: String) {
    init {
        // initializer block ...
    }
}

보조 생성자

두 번째 생성자부터는 클래스 바디에 정의합니다. 기본 생성자가 정의되어 있는 경우에는 기본 생성자로부터 또는 다른 보조 생성자로부터 값을 위임 받아야 합니다.

기본 생성자가 없는 경우라도, 암묵적으로 위임 처리가 진행되며 초기화 블록도 실행됩니다.

class Person (name: String) {
    init {
        // initializer block ...
    }
    constructor (name: String, age: Int) : this(name) {
        // ...
    }
}

기본 생성자도 보조 생성자도 정의되지 않은 클래스의 경우, 매개 변수가 없는 기본 생성자가 자동으로 생성되며, 자동으로 만들어진 생성자는 public의 접근 권한으로 지정됩니다.

객체의 인스턴스화

코틀린에는 new 키워드가 없습니다.

val charlie = Person("Charlie")

프로퍼티

var 또는 val을 사용해서 클래스의 프로퍼티를 선언합니다.

class Person {
    val name: String
    var age: Int
}

프로퍼티의 겟터와 셋터를 지정할 수 있습니다.

class SomeClass {
    var someProperty: String
        get() = this.toString()
        set(value) {
            // ...
        }
}

겟터와 셋터에 접근한정자를 지정하거나 어노테이션을 지정하는 경우에 다음과 같이 할 수 있습니다.

var someProperty: String = "abcd"
    private set // 셋터는 기본 구현을 사용하며, 접근은 private으로 제한

필드

field는 프로퍼티의 값을 임시로 저장해두는 공간입니다. field는 프로퍼티의 접근자에서만 사용할 수 있습니다.

var counter = 0
    set(value) {
        if (value >=0) field = value
        // counter = value 라고 하면 안됩니다.
    }

컴파일 타임 상수

컴파일 타임에만 사용되는 상수에는 const 키워드를 붙여서 컴파일 타임 상수로 만듭니다.

const val MY_VALUE: String = "..."

지연 초기화 프로퍼티 또는 변수

lateinit var subject: Subject

추상 클래스

추상 클래스는 abstract를 사용해서 정의합니다. 추상 클래스는 상속을 전제로 하므로 open을 명시할 필요가 없습니다.

오버라이드하는 함수 앞에는 override 키워드를 붙입니다.

abstract class Shape {
    abstract fun draw()
}

class Rectangle : Shape() {
    override fun draw(){
        // ...
    }
}

추상 클래스가 아닌 클래스를 상속하는 추상 클래스도 만들 수 있습니다.

open class Shape {
    open fun draw(){
        // ...
    }
}

abstract class SomeShape : Shape() {
    abstract override fun draw()
}

인터페이스

인터페이스는 구현된 메서드를 가질 수도 있으며, 프로퍼티를 가질 수도 있습니다.

interface MyInterface {
    val prop1: Int // 추상 프로퍼티
    val prop2: String // 겟터가 구현된 프로퍼티
        get() = "foo"
    fun bar()
    fun foo() {
        // 바디를 가질 수도 있습니다.
    }
}

다중 상속시 충돌의 해결

interface A {
    fun foo() { print("A") }
    fun bar()
}
interface B {
    fun foo() { print("B") }
    fun bar()
}

class C : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
    override fun bar(){
        print("C")
    }
}

상속

클래스의 상속은 :을 사용해서 표현합니다.

클래스는 기본적으로 final이므로 상속 가능하게 하려면 클래스 선언 앞에 open을 지정해야 합니다.

open class Shape

class Rectangle(var height: Double, var length: Double): Shape(){
    var perimeter = (height + length) * 2
} 
open class Base(p: Int)

class Derived(p: INt) : Base(p)

만일, 클래스에 기본 생성자가 없다면 각각의 보조 생성자에서 super를 통해서 호출해야 합니다.

class Dog : Animal {
    constructor(name: String) : super(name)
    constructor(name: String, age: Int) : super(name, age)
}

상속 가능한 클래스와 오버라이드 가능한 메서드 앞에는 open을 붙이니다. 그리고, 오버라이드 하는 메서드 앞에는 override를 붙입니다.

오버라이드 한 메서드는 open상태이므로, 자식 클래스에서 오버라이드를 제한하려면 final을 붙여야 합니다.

open class Rectangle() : Shape() {
    final override fun draw() {
        // ...
    }
}

메서드와 마찬가지 방식으로 프로퍼티도 오버라이드할 수 있습니다.

open class Shape {
    open val vertexCount: Int = 0
}

class Rectangle : Shape() {
    override val vertexCount = 4
}

Data 클래스

data class User(val name: String, val age:Int)

Sealed 클래스

sealed interface Error
sealed class IOError(): Error

Inner 클래스

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

인라인 클래스

@JvmInline
value class Password(private val s: String)