달나라 노트

Kotlin - Inheritance (상속) 본문

Kotlin

Kotlin - Inheritance (상속)

CosmosProject 2021. 3. 15. 03:13
728x90
반응형

 

 

 

 

Original source = play.kotlinlang.org/byExample/01_introduction/01_Hello%20world 

 

 

 

open class Dog {
    open fun Hello() {
        println("Hello This is a Dog.")
    }
}

class Cat : Dog() {

}

fun main() {
    var x = Cat()

    x.Hello()
}


-- Result
Hello I am Dog.

Kotlin도 상속이란 개념이 있습니다.

Dog class를 Cat에 상속시키고있습니다.

이것은 Cat : Dog() 처럼 세미콜론을 이용하여 가능합니다.

 

main 함수 부분을 보면 x는 Cat class를 이용하여 객체가 되고 있습니다.

Cat class는 아무 내용이 없었고 원래대로라면 x도 아무 method가 없어야합니다.

하지만 Cat class는 Dog class를 상속받았기 때문에 Hello라는 method를 사용할 수 있습니다.

 

또한 Dog class를 보면 open class, open fun이라고 적었는데 상속을 해주기 위해선 open이라는 키워드를 이용하여 class와 function을 생성해야 합니다.

 

 

주의!

Kotlin에서 상속이 가능한 class는 open class 또는 abstract class뿐입니다.

 

 

 

 

 

open class Dog {
    open fun Hello() {
        println("Hello This is a Dog.")
    }
}

class Cat : Dog() {
    fun Hello() {
        println("Hello This is a Cat.")
    }
}

fun main() {
    var x = Cat()

    x.Hello()
}

만약 Cat에도 Dog에 있는 method와 동일한 이름의 Hello라는 method를 가지고 있다면 상속할 때 어떻게 될까요?

위처럼 코드를 적으면 Dog class를 Cat class에게 상속해줄 때,

Dog class의 Hello method가 Cat class의 Hello method를 덮어씌울 거나

그 반대로 Cat class의 Hello method가 Dog class의 Hello method를 덮어씌울 것 같습니다.

하지만 위 코드는 에러를 일으킵니다.

 

 

 

 

 

 

open class Dog {
    open fun Hello() {
        println("Hello This is a Dog.")
    }
}

class Cat : Dog() {
    override fun Hello() {
        println("Hello This is a Cat.")
    }
}

fun main() {
    var x = Cat()

    x.Hello()
}


-- Result
Hello This is a Cat.

위 코드가 정상적으로 작동하려면 상속받는 Hello method에 override라는 키워드를 적어줘야 합니다.

그러면 상속할 때 이름이 겹치는 method가 있을 경우 override가 적힌 상속받는 Cat class의 Hello method가 상속해주는 부모 class인 Dog class의 Hello method를 덮어씌웁니다.

결국 Hello method는 Cat class에 있는 Hello method가 최종적으로 남게 된다는 뜻이죠.

 

 

 

 

open class Fruit(var x: String) {
    fun WhatisThis() {
        println("This is $x")
    }
}

class Apple() : Fruit("apple") {

}

fun main() {
    var apple: Apple = Apple()
    apple.WhatisThis()
}


-- Result
This is apple

만약 상속해주는 부모 class(Fruit class)가 parameter를 받는다면

자식 class(Apple class)에게 상속해줄 때 parameter를 동시에 전달해주면 됩니다.

 

 

 

 

open class Fruit(var x: String, var y: Int) {
    fun WhatisThis() {
        println("This is $x and I have $y of it.")
    }
}

class Apple(cnt: Int) : Fruit(x = "apple", y = cnt) { // 1

}

fun main() {
    var apple: Apple = Apple(10)
    apple.WhatisThis()
}


-- Result
This is apple and I have 10 of it.

위 코드도 마찬가지로 Fruit class를 Apple class에 상속시키고 있습니다.

근데 좀 다른 점이 있습니다.

Fruit class는 x, y 2개의 parameter를 받아야 하고 상속해줄 때에도 2개의 parameter를 전달하면서 상속시켜야 합니다.

근데 1번 부분을 보면 x는 apple이라고 값을 전달하지만 y는 cnt라는 변수를 전달했습니다.

이것은 상속받는 Apple class의 parameter인 cnt를 Fruit class의 y parameter로 사용하라는 뜻입니다.

 

그래서 apple 객체를 Apple class를 이용하여 만들 때 Apple class의 parameter로 10을 전달하면,

cnt = 10이 되고

cnt는 y로 전달되는 형식으로 진행됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

위 내용들을 기초로 하여 복습의 개념으로 예시 하나를 더 봐봅시다.

open class test_cl() { // 1
    var str_laptop: String = "MacBookPro16"
    
    fun showLaptop() { // 1-1
        println(str_laptop)
    }
}

class test_cl2(name: String, team: String): test_cl() { // 2
    var str_name: String = "Name: ${name}"
    var str_team: String = "Team: ${team}"
    
    fun showInfo() {
        println(str_name)
        println(str_team)
    }
}

fun main() {
    var test = test_cl()
    var test2 = test_cl2("Bella", "Team1")
    
    println(test)
    println(test2.str_name)
    println(test2.str_team)
    println(test2.str_laptop) // 3
    
    test2.showInfo()
    test2.showLaptop() // 4
}



-- Result
test_cl@548c4f57
Name: Bella
Team: Team1
MacBookPro16
Name: Bella
Team: Team1
MacBookPro16

1. test_cl class를 생성합니다.

test_cl class를 test_cl2 class에 상속시킬 것이기 때문에 open class로 생성합니다.

(만약 open 키워드를 붙이지 않으면 상속해주는 것이 불가능합니다.)

 

1-1. test_cl class를 test_cl2에 상속시킬 때 test_cl2에는 showLaptop이라는 이름의 method가 없습니다.

따라서 이 경우엔 test_cl class의 showLaptop method에 open 키워드를 추가해줄 필요가 없습니다.

 

2. test_cl2 class를 생성합니다. 이때 test_cl class를 상속받습니다.

이 경우 test_cl2 class는 test_cl2에서 선언된 atrribute, method와 더불어 test_cl class의 attribute, method를 추가로 가지게 됩니다.

 

3. 2번의 결과로 test_cl2 class에서는 str_laptop이라는 attribute를 적어주지 않았지만 test_cl class로부터 상속받은 str_laptop attribute를 갖게 됩니다.

 

4. 마찬가지입니다. test_cl2에는 showLaptop이라는 method가 없었지만 test_cl을 상속받았기 때문에 test_cl class에 있던 showLaptop method를 사용할 수 있게 되었습니다.

 

 

 

 

 

 

 

open class test_cl() {
    var str_laptop: String = "MacBookPro16"
    
    open fun showLaptop() { // 1
        println(str_laptop)
    }
}

class test_cl2(name: String, team: String): test_cl() {
    var str_name: String = "Name: ${name}"
    var str_team: String = "Team: ${team}"
    
    fun showInfo() {
        println(str_name)
        println(str_team)
    }
    
    override showLaptop() { // 2
        println("Laptop")
    }
}

fun main() {
    var test = test_cl()
    var test2 = test_cl2("Bella", "Team1")
    
    println(test)
    println(test2.str_name)
    println(test2.str_team)
    println(test2.str_laptop)
    
    test2.showInfo()
    test2.showLaptop() // 3
}



-- Result
test_cl@548c4f57
Name: Bella
Team: Team1
MacBookPro16
Name: Bella
Team: Team1
Laptop

위 예시를 봅시다.

바로 이전에 봤던 예시에서 약간 수정을 하였습니다.

 

1. showLaptop function에 open 키워드를 붙였습니다.

그 이유는 test_cl class를 test_cl2에 상속시키면서 test_cl2에도 동일한 이름의 showLaptop method가 있기 때문입니다.

 

2. showLaptop method가 test_cl에 존재합니다. test_cl의 showLaptop이 test_cl2로 상속되면서 override됩니다.

그러면 test_cl2의 showLaptop이 더 우선적으로 남게됩니다.

 

3. 2번의 결과로 test_cl2를 이용하여 만든 test2 객체의 showLaptop method를 사용하면 test_cl2에서 정의된 showLaptop method(println("Laptop"))가 실행됩니다.

 

 

 

여기서 주의할 점은 2가지입니다.

1. 상속을 해 줄 부모 class에는 open 키워드를 적어줘야 한다.

2. 부모 class의 어떤 method A와 동일한 이름의 method A가 자식 class에도 있다면 해당 method A에 대해서 부모 class method A에는 open 키워드를, 자식 class의 method A에는 override를 적어주어야 합니다.

3. 2번처럼 부모 class의 method와 자식 class의 method가 충돌할 경우 자식 class method가 최종적으로 남아있게 됩니다.(부모 class의 method는 상속 과정에서 자식 class의 method가 덮어씌웁니다.)

 

 

 

 

 

 

여기까지 하여 상속의 개념을 알아봤습니다.

근데 상속은 왜 사용할까요?

굳이 왜 필요할까요?

 

이론적으로만 말하자면 사실 필요 없습니다.

상속이란 개념이 없어도 각 class마다 원하는 기능을 넣어서 객체를 생성하고 사용하는 등 원하는 것은 모두 할 수 있습니다.

 

하지만 코드가 길어진다면 위와 같은 방법은 상당히 불편할겁니다.

 

예를 들어보겠습니다.

 

A class

B class

C class

D class

F class

G class

H class

I class

J class

K class

 

위처럼 당신의 프로그램에 A class부터 K class까지 총 10개의 class를 작성했다고 합시다.

각 class는 당연히 조금씩 다를겁니다. 그러니까 10개의 서로 다른 class를 생성했겠지요.

하지만 10개의 class에는 공통된 method나 속성값이 존재한다고 가정해봅시다.

예를들어 모두 아래와 같은 내용을 공통적으로 가지고 있고 여기에 추가로 각 class별로 다른 고유의 method나 attribute를 가지고 있는 모양입니다.

 

val muliply_factor = 10

fun test_fun(x) {
    return x * muliply_factor
}

 

 

근데 프로그램을 운영하다보니 muliply_factor가 10에서 11로 변경되어야 한다고 가정합시다.

 

그러면 코드를 수정해야하고 당시는 10개의 class 각각에 대해서 아래처럼 수정을 해야합니다.

val multiply_factor = 10 -> val multiply_factor = 11

 

 

근데 여기서 끝이 아닙니다.

보다보니 새로운 기능이 필요해서 새로운 method를 추가해야하는데 이 method는 존재하는 A ~ K의 10개의 class가 공통으로 필요로 한다고합니다.

그러면 다시 10개 class에 원하는 method 내용을 각각 적어줘야합니다.

 

 

즉, 어떤 공통적인 인자들에 변경이 생길 때 마다 당신은 10번씩 코드를 지우고, 쓰고, 수정해야합니다.

 

 

그러면 이제 상속이란 개념을 이용해보죠.

 

Z class -> A class

Z class -> B class

Z class -> C class

Z class -> D class

Z class -> F class

Z class -> G class

Z class -> H class

Z class -> I class

Z class -> J class

Z class -> K class

 

위처럼 A ~ K class가 공통으로 가지는 내용을 Z class에 적어두고 Z class를 A ~ K class에 상속해주는 방식으로 코드를 수정했습니다.

이렇게 상속하는 과정에서 A ~ K class가 가지는 특수한 개별적인 내용은 따로 설정해주죠.

 

이 상황에서 위에서 생겼던 변경사항을 반영하려면 어떻게하면 될까요?

 

눈치채셨겠지만 공통적인 요소는 모두 Z class로부터 상속받고 있기 때문에 Z class만 수정해주면 그 수정사항이 상속받는 자식 class들(A ~ K class)에 적용됩니다.

 

상속을 사용하는 이유야 찾고 찾아보면 여러 가지가 있겠지만 대표적으로 이처럼 아주 긴 코드를 이용할 때 상당히 유용하게 사용될 수 있습니다.

 

 

 

 

 

728x90
반응형

'Kotlin' 카테고리의 다른 글

Kotlin - Loops  (0) 2021.03.15
Kotlin - when 구문  (0) 2021.03.15
Kotlin - Class  (0) 2021.03.15
Kotlin - Null  (0) 2021.03.15
Kotlin - Variable  (0) 2021.03.15
Comments