본문 바로가기
2024 활동/Kotlin·Android

[Kotlin] 클래스 심화 : 상속, 오버라이딩, 형변환, 스마트 캐스트

by 은행장 노씨 2024. 4. 18.

 

클래스를 응용하여 할 수 있는 다양한 기술들을 정리했다. 

 


1. 상속

클래스의 꽃은 상속이지..

이번 시간에는 코틀린 클래스의 상속에 대해 정리해보겠다. 

 

튜터님이 말씀하셨다.

⚠️ 상속은 공통점 찾기가 아니다!
상속은 원래 기능을 받고, 스페셜 기능을 추가하고 싶을 때 쓰는 것...

곽철용의 마인드로 상속을 이해해보자.

  • 슈퍼클래스/ 서브클래스, 부모클래스/자식클래스
    • 서브클래스는 슈퍼클래스의 생성을 책임져야 한다.
    • 슈퍼클래스 사용하고 있는 동일한 이름의 멤버를 만들 수 없다.
    • 상속은 여러 번 할 수 있다. 다중상속은 불가능하다.

 

왜 코틀린에서 다중상속을 할 수 없나요?
: 다이아몬드 문제 상속이 복잡성과 모호성을 야기함. -> 코드의 유지보수를 어렵게 만듦
- 코틀린은 대신 인터페이스를 통해 다중 상속과 유사한 기능을 제공함.

 

❓ 다중상속과 인터페이스의 차이가 뭔가요?
다중상속은 상속받은 메소드에 대한 구현을 그대로 사용할 수 있다
반면, 인터페이스는 메소드의 시그니처만 제공하며 구현은 각 클래스에서 해야 한다.

 

❓ 다중상속의 장단점은 뭔가요?
- 장점 : 재사용성 향상, 기능 통합
- 단점 : 복잡성 증가, 다이아몬드 문제, 유지보수의 어려움.

 

  • 코틀린의 모든 클래스는 Any라는 최상위 클래스로부터 상속을 받는다. 
  • 상위 클래스가 open 키워드로 선언되야 한다. (클래스나 메소드가 상속 혹은 오버라이드 가능함)

 

1-1. Any Class

public open class Any public constructor() {
    public open operator fun equals(other: kotlin.Any?): kotlin.Boolean { /* compiled code */ }

    public open fun hashCode(): kotlin.Int { /* compiled code */ }

    public open fun toString(): kotlin.String { /* compiled code */ }
}

궁금해서 Any 클래스를 찾아봤다. equals, hashCode, toString 메서드가 정의되어 있다. 

이게 기본 메소드인가 보다. 

 

1-2. 상속 예시 코드

open class Animal(val name: String) {
    open fun sound() {
        println("This animal makes a sound.")
    }
}

class Dog(name: String) : Animal(name) {
    override fun sound() {
        println("$name says: Woof Woof")
    }
}

- 슈퍼클래스 키워드로 open을 명시해줘야 한다

 

 

2. 오버라이딩(Overriding)

⚠️ 슈퍼 클래스가 사용하고 있는 멤버와 동일한 이름의 멤버를 만들 수 없다.
→ 오버라이딩을 통해서 슈퍼클래스가 가지고 있는 함수를 재정의 할 수 있다.

 

오버로딩(Overloading)은 뭔가요?
하나의 클래스 내에서 같은 이름의 메소드를 여러 개 가지면서 각 메소드가 다른 매개변수를 가지는 것
1. 함수가 받는 인자(파라미터)가 다른 경우 -> 사용 가능
2. 함수가 리턴 타입이 다른 경우 → 사용 불가능(코틀린 추론을 안 해준당)

 

2-1. 오버로딩 예시

class Sum10 {
    fun sum(): Int {
        return 10
    }
    fun sum(number1 : Int): Int {
        return number1 + 10
    }
    fun sum(number1: Int, number2: Int): Int {
        return number1 + number2 + 10
    }
}

val sum = Sum10()
println(sum.sum())
println(sum.sum(10))
println(sum.sum(2, 3))

 

2-1. 오버라이딩 예시

open class Animal(val name: String) {
    open fun speed(): Int {
        return 10  // 모든 동물의 기본 이동 속도
    }
}

class Dog(name: String) : Animal(name) {
    override fun speed(): Int {
        return super.speed() + 20  // 개는 기본적인 동물보다 이동 속도가 20 빠르다
    }
}

class Turtle(name: String) : Animal(name) {
    override fun speed(): Int {
        return super.speed() - 8  // 거북이는 기본적인 동물보다 이동 속도가 8 느리다
    }
}

 

 

 

3. 형변환(Type Casting) 

클래스의 형변환을 알아보자. 

- 형변환을 사용하면 특정 타입의 객체를 다른 타입으로 다룰 수 있다. 

슈퍼클래스 ← 서브클래스(O)
서브클래스는 슈퍼클래스의 모든 특성을 상속받는다. (업캐스팅) 항상 안전함.
슈퍼클래스 → 서브클래스(X)
슈퍼클래스 객체를 서브클래스 타입으로 변환시키는 것은 기본적으로 허용되지 않는다. (다운캐스팅)
실제 객체가 해당 서브 클래스의 인스턴스일 때만 가능함. (안그러면 런타임 오류)

 

3-1. smart casting(1) : is

is 연산자는 객체가 특정 타입인지 검사하고, 조건이 참이면 해당 범위 내에서 객체를 그 타입으로 취급한다. 

open class Animal(val name: String)

class Bird(name: String) : Animal(name) {
    fun fly() {
        println("$name can fly high.")
    }
}

fun main() {
    val animal: Animal = Bird("Tweety")
    if (animal is Bird) {
        animal.fly()  // 스마트 캐스팅, Bird 타입의 메소드 fly() 호출
    }
}

 

3-2. smart casting(2) : as

as 연산자를 통해 객체를 특정 타입으로 강제로 변환한다.

이 때, 실제 객체의 타입이 캐스팅하려는 타입과 호환되지 않으면 ClassCastException이 발생한다.

open class Animal(val name: String)

class Fish(name: String) : Animal(name) {
    fun swim() {
        println("$name swims in the water.")
    }
}

fun main() {
    val animal: Animal = Fish("Goldie")
    val fish = animal as Fish  // 명시적 캐스팅
    fish.swim()  // Fish 타입의 메소드 swim() 호출
}

 

+ ) 'as?' 사용한다면 안전하게 형변환을 할 수 있다.

val animal: Animal = Fish("Goldie")
val fish = animal as? Fish  // 안전한 형변환, 실패 시 null 반환
fish?.swim()  // null이 아닐 경우 Fish 타입의 메소드 swim() 호출

 

 


정리해보자

클래스의 상속, 오버라이딩, 형변환은 객체 지향 프로그램의 핵심이다.

  1. 상속:
    코틀린에서 상속은 기존 클래스의 기능을 확장하고 특수 기능을 추가할 때 사용함.
    - 재사용을 촉진하고, 계층적인 클래스 구조를 만듦
    - 단, 코틀린은 다중상속을 지원하지 않으며, 대신 인터페이스를 통해 유사한 기능을 제공함.
  2. 오버라이딩과 오버로딩:
    메소드 오버라이딩은 상속받은 메소드의 기능을 자식 클래스에서 재정의하는 것
    - 오버로딩은 같은 이름의 메소드를 다른 매개변수로 여러 개 정의하여 사용할 수 있는 기능
    - 슈퍼클래스에서는 open 키워드, 서브클래스에서는 override 키워드로 명시해줘야 함. 
  3. 형변환:
    코틀린에서는 is, as, as? 연산자를 통해 객체의 타입을 검사하고 안전하게 변환할 수 있음.

 

 

이렇게 응용하는 게 진짜 재밌는 것 같다.