Kotlin 기본[5] - 상속, 다형성, 프로퍼티

하동혁 ·2023년 4월 2일
0

Kotlin

목록 보기
5/10
post-thumbnail

▪️ 상속

  • 모든 클래스는 Any의 하위클래스이며, 기본적으로 상속 줄 수 없는 final class로 만들어진다.
  • 파생클래스를 허용하려면 open 키워드를 사용하여 상속가능한 상태로 선언해야 한다.
  • extends 키워드 대신 콜론( : )을 사용한다.
  • open 키워드를 통한 선언
  • 하위 클래스는 부모의 프로퍼티나 메서드를 받아서 오버라이딩 하여 재장성 가능하다.
//  상속 가능한 클래스를 위해 open 사용
open class Human (var name: String="홍길동", var age: Int) { // 주생성자
    open fun play() = println("name: $name")
    fun sing(vol: Int) = println("Sing age: $age")
}


//  주 생성자를 사용하는 상속
class Woman (name: String, age: Int) : Human(name, age) {
    fun singHitone() = println("Happy Song!") // 새로 추가된 메서드
    override fun play() = println("Woman name: $name")
}

//  부 생성자를 사용하는 상속
class Man : Human {
    val race: String
    constructor(name: String, age: Int, race: String) : super(name, age) {
        this.race = race // 새로 추가된 프로퍼티
    }
}

fun main() {
    var woman = Woman("옥분이", 22)
    woman.play()
    var man = Man("옥철이", 20, "아시아")
}

▪️ super

  • super는 상위 클래스의 메서드, 프로퍼티, 생성자를 사용하는 키워드
    • super.메서드이름
    • super.프로퍼티
    • super()

▪️ this

  • this는 현재 클래스의 메서드, 프로퍼티, 생성자를 사용하는 키워드
    • this.메서드이름
    • this.프로퍼티
    • this()


▪️ 다형성 (Polymorphism)

  • Type Polymorphism (타입추론으로 사용할 수도 있다.)
  • Method Polymorphism
  • 오버라이딩
    • 상속 받아서 선언부는 동일하나 구현부는 다르게 바꾸어 재설계 가능
  • 오버로딩
    • 함수 명은 동일하나 인자를 다르게 하여 여러 경우를 처리
open class Human {
    fun play() { println("Human.play()") }
    open fun sing() { println("Human.sing()") }
    open fun sing2() { println("Human.sing2()") }
}

open class Animal01 : Human(){
    override fun sing(){ println("Animal01.sing()") }
    final override fun sing2(){ println("Animal01.sing2()") }
}

open class Animal02 : Animal01(){
    override fun sing(){ println("Animal02.sing()") }
}

fun main() {
    var animal = Animal02()
    animal.play()  // Human.play()
    animal.sing()  // Animal02.sing()
    animal.sing2()  // Animal01.sing2()
}


▪️ Property

  • 자바의 필드(Fields)
    • 단순한 변수 선언만 가지기 때문에 접근을 위한 메서드를 따로 만들어야 함
    • setter, getter를 개발자가 생성

  • 코틀린의 프로퍼티(Properties)
    • 변수 선언과 기본적인 접근 메서드를 모두 가지고 있음
    • setter, getter는 내부적으로 생성하게 됨
// 주 생성자에 3개의 매개변수 정의
class User(_id: Int, _name: String, _age: Int) {
    // 프로퍼티들    
    val id: Int = _id // 불변 (읽기 전용)
    var name: String = _name // 변경 가능
    var age: Int = _age // 변경 가능
} 
class User(val id:Int, var name:String, var age:Int) { // 프로퍼티들

}

▪️ setter, getter를 직접 지정할 수 있다.

  • 불변형인 val은 getter만 설정 가능하다.
class Person {
    private var _name: String = ""
    
    var name: String
        get() = _name
        set(value) {
            _name = value
        }
}

fun main() {
    val person = Person()
    person.name = "John"
    println(person.name)
}

▪️ backing field 사용

Kotlin에서 프로퍼티(property)는 값을 저장하는 backing field와 그 값을 읽고 쓸 수 있는 getter와 setter로 구성됩니다. backing field를 사용하지 않는 경우, getter와 setter에서 직접 값을 저장하거나 반환할 수 있습니다.

하지만 이러한 방식으로 프로퍼티를 구현할 경우, 일부 예상치 못한 동작이 발생할 수 있습니다. 예를 들어, 아래와 같은 클래스가 있다고 가정해보겠습니다.

class Person {
    var name: String = ""
        set(value) {
            println("Setting name to $value")
            field = value
        }
        get() {
            println("Getting name")
            return "Mr. $field"
        }
}

위 코드에서는 name 프로퍼티에 getter와 setter 함수를 구현하고 있습니다. 하지만 이 때 backing field를 사용하지 않고, getter에서는 "Mr. "을 붙여서 반환하고 있습니다.

이 상태에서 Person 객체를 생성하고, name 프로퍼티를 두 번 읽으면 다음과 같은 결과가 출력됩니다.

fun main() {
    val person = Person()
    person.name = "John"
    println(person.name)
    println(person.name)
}


Setting name to John
Getting name
Mr. John
Getting name
Mr. John

이 결과에서 알 수 있듯이, getter 함수에서 field 대신에 직접 "Mr. "과 field를 조합하여 반환하면, 호출시 예상했던 John의 이름이 아닌 앞에 Mr.이 붙은 예상치 못한 결과를 얻게 됩니다.

따라서 Kotlin에서는 backing property를 사용하여 getter와 setter 함수를 구현하는 것이 좋습니다. 이렇게 하면 예상한 대로 동작하며, 코드의 가독성과 유지 보수성이 향상됩니다.

0개의 댓글