본문 바로가기
Kotlin

Kotlin #6 : 상속과 클래스 230217

by haheehee 2023. 2. 20.

++ 상속(inheritance) ++

- 자식 클래스를 만들 때 상위 클래스(부모 클래스)의 속성과 기능을 물려받아 계승

- 상위(부모) 클래스의 프로퍼티와 메서드가 자식에 적용됨

 

- open 키워드를 통한 선언

open class 기반 클래스명 { // open으로 파생 가능 (다른 클래스가 상속 가능한 상태가 됨)
	...
}
class 파생 클래스명 : 기반 클래스명() { // 기반 클래스로부터 상속, 최종 클래스로 파생 불가
	...
}

** 코틀린의 모든 클래스는 묵시적으로 Any로부터 상속 (자바에서의 Object가 코틀린의 Any라고 생각하면 된다.)

 

super(...) : 상위클래스 생성자 호출

 

++ 다형성(polymorphism) ++

- 같은 이름을 사용하지만 구현 내용이 다르거나 매개변수가 달라서 하나의 이름으로 다양한 기능을 수행할 수 있는 개념

 

++ 오버라이딩(overriding) ++

- 기능을 완전히 다르게 바꾸어 재설계

** 누르다 -> 행위 -> push()

- push()는 '확인' 혹은 '취소' 용도로 서로 다른 기능을 수행 할 수 있음

- 상속과 관련

 

++ 오버로딩(overloading) ++

- 기능은 같지만 인자를 다르게 하여(파라미터를 다르게 하여) 여러 경우를 처리

** 출력한다 -> 행위 -> print()

- print(123), print("Hello") 인자는 다르지만 출력의 기능은 동일함

 

- 오버로딩의 일반함수의 예시

// 정수 자료형 매개변수 2개를 더함
fun add(x: Int, y: Int): Int { 
	return x + y
}

// 실수 자료형 매개변수 2개를 더함
fun add(x: Double, y: Double): Double { 
	return x + y
}

// 정수 자료형 매개변수 3개를 더함
fun add(x: Int, y: Int, z: Int): Int { 
	return x + y + z
}

package com.example.pp129

// 상속 가능한 클래스를 위해 open 사용
open class Bird(var name: String, var wing: Int, var beak: String, var color: String) {
    // 메서드
    fun fly() = println("Fly wing: $wing")
    fun sing(vol: Int) = println("Sing vol: $vol")
}

// 주 생성자를 사용하는 상속
class Lark(name: String, wing: Int, beak: String, color: String) : Bird(name, wing, beak, color) {
    fun singHitone() = println("Happy Song!")   // 새로 추가된 메서드
}

// 부 생성자를 사용하는 상속
class Parrot: Bird {
    val language: String

    constructor(name: String, wing: Int, beak: String, color: String, language: String) : super(name, wing, beak, color) {
        this.language = language // 새로 추가된 프로퍼티
    }
    fun speak() = println("Speak! $language")
}

fun main() {
    val coco = Bird("mybird", 2, "short", "blue")
    val lark = Lark("mylark", 2, "long", "brown")
    val parrot = Parrot("myparrot", 2, "short", "multiple", "korean")   // 프로퍼티가 추가됨
    
    println("Coco: ${coco.name}, ${coco.wing}, ${coco.beak}, ${coco.color}")
    println("Lark: ${lark.name}, ${lark.wing}, ${lark.beak}, ${lark.color}")
    println("Parrot: ${parrot.name}, ${parrot.wing}, ${parrot.beak}, ${parrot.color}, ${parrot.language}")
    
    lark.singHitone()   // 새로 추가된 메서드가 사용
    parrot.speak()
    lark.fly()
}

파생 클래스 만들어보기

- 하위 클래스는 상위 클래스의 메서드나 프로퍼티를 그대로 상속하면서 

상위 클래스에는 없는 자신만의 프로퍼티나 메서드를 확장

 

package com.example.pp134

fun main() {
    val calc = Calc()
    println(calc.add(3, 2))
    println(calc.add(3.2, 1.3))
    println(calc.add(3, 3, 2))
    println(calc.add("Hello", "World"))
}

class Calc {
    // 다양한 매개변수로 오버로딩된 메서드들
    fun add(x: Int, y: Int): Int = x + y
    fun add(x: Double, y: Double): Double = x + y
    fun add(x: Int, y: Int, z: Int): Int = x + y + z
    fun add(x: String, y: String): String = x + y
}

클래스 메서드의 오버로딩

- 덧셈 동작의 오버로딩

 


++ 오버라이딩 (Overriding) ++

- ‘(기존의 작업을) 중단하다’, ‘뒤엎다’ 등으로 해석

- 상위 클래스의 메서드의 내용을 완전히 새로 만들어 다른 기능을 하도록 정의

- 오버라이딩하기 위해 부모 클래스에서는 open 키워드,

    자식 클래스에서는 override 키워드를 각각 이용

- 메서드 및 프로퍼티등에 사용할 수 있다.

 

- 메서드 오버라이딩 예시

open class Bird { // 여기의 open은 상속 가능을 나타냄
	...
	fun fly() { ... } // 최종 메서드로 오버라이딩 불가
	open fun sing() {...} // sing() 메서드는 하위 클래스에서 오버라이딩 가능
}

class Lark() : Bird() { // 하위 클래스
	fun fly() { /* 재정의 */ } // 에러! 상위 메서드에 open키워드가 없어 오버라이딩 불가
	override fun sing() { /* 구현부를 새롭게 재정의 */ } // 구현부를 새롭게 작성
}

 

- 오버라이딩 금지

** 파생 클래스에서 오버라이딩을 금지할 때

open class Lark() : Bird() {
	final override fun sing() { /* 구현부를 새롭게 재정의 */ } // 하위 클래스에서 재정의 금지
}

package com.example.pp138

// 상속 가능한 클래스를 위해 open 사용
open class Bird(var name: String, var wing: Int, var beak: String, var color: String) {
    // 메서드
    fun fly() = println("Fly wing: $wing")
    open fun sing(vol: Int) = println("sing vol: $vol") // 오버라이딩 가능한 메서드
}

class Parrot(name: String, wing: Int = 2, beak: String, color: String,  // 마지막 인자만 var로 선언되어 프로퍼티가 추가됨
            var language: String = "natural"): Bird(name, wing, beak, color) {
    fun speak() = println("Speak! $language")  // Parrot에 추가된 메서드
    override fun sing(vol: Int) { // 오버라이딩된 메서드
        println("I'm a parrot! The volume level is $vol")
        speak() // 달라진 내용
    }
}

fun main() {
    val parrot = Parrot(name="myparrot", beak="short", color="multiple")
    parrot.language = "English"
    println("Parrot: ${parrot.name}, ${parrot.wing}, ${parrot.beak}, ${parrot.color}, ${parrot.language}")
    parrot.sing(5)  // 달라진 메서드 실행 기능
}

메서드 오버라이딩 하기

 


++ super, this ++

- 현재 클래스에서 참조의 기능

- 상위 클래스는 super 키워드로 현재 클래스는 this 키워드로 참조가 가능

 

super

  • super.프로퍼티명 // 상위 클래스의 프로퍼티 참조
  • super.메서드명( ) // 상위 클래스의 메서드 참조
  • super( ) // 상위 클래스의 생성자의 참조

this

  • this.프로퍼티명 // 현재 클래스의 프로퍼티 참조
  • this.메서드명( ) // 현재 클래스의 메서드 참조
  • this( ) // 현재 클래스의 생성자의 참조

package com.example.pp140

open class Bird100(var name: String, var wing: Int, var beak: String, var color: String) {
    fun fly() = println("Fly wing: $wing")
    open fun sing(vol: Int) = println("Sing vol: $vol")
}

class Parrot100(name: String, wing: Int = 2, beak: String, color: String,
                var language: String = "natural") : Bird100(name, wing, beak, color) {
    fun speak() = println("Speak! $language")
    override fun sing(vol: Int) { // 부모의 내용과 새로 구현된 내용을 가짐
        super.sing(vol) // 상위 클래스의 sing()을 먼저 수행
        println("I'm a parrot! The volume level is $vol")
        speak()
    }
}

fun main() {
    val parrotmo = Parrot100("leemiso", 4, "long", "orange")
    parrotmo.sing(4)
}

super로 상위 참조

- 상위 클래스의 메서드 실행

 

package com.example.pp142

open class Person {
    constructor(firstName: String) {
        println("[Person] firstName: $firstName")
    }
    constructor(firstName: String, age: Int) {
        println("[Person] firstName: $firstName, $age")
    }
}

class Developer: Person {
    constructor(firstName: String): this(firstName, 10) {
        println("[Developer] $firstName")
    }
    constructor(firstName: String, age: Int): super(firstName, age) {
        println("[Developer] $firstName, $age")
    }
}

fun main() {
    val hahee = Developer("Hahee")
}

this와 super를 사용하는 부 생성자

 

package com.example.pp143

class Person(firstName: String,
            out: Unit = println("[Primary Constructor] Parameter")) {   // 주 생성자
    val fName = println("[Property] Person fName: $firstName")  // 프로퍼티 할당

    init {
        println("[init] Person init block") // 초기화 블록
    }

    // 보조 생성자
    constructor(firstName: String, age: Int,
                out: Unit = println("[Secondary Constructor] Parameter")): this(firstName) {
        println("[Secondary Constructor] Body: $firstName, $age")   // 부 생성자 본문
    }
}

fun main() {
    val p1 = Person("Kildong", 30)
    println()
    val p2 = Person("Dooly")
}

주 생성자와 부 생성자 함께 사용하기

** 순서 꼭 알아야함. (처리 로직)

 


++ 바깥 클래스 호출하기 ++

- @(엣) 기호의 이용

- 이너 클래스에서 바깥 클래스의 상위 클래스를 호출하려면

    super 키워드와 함께 엣(@) 기호 옆에 바깥 클래스명을 작성해 호출

 

*** 인터페이스는 소괄호 xxxxxx 

     interface B {...}

     class C : A(), B {...}


package com.example.pp146

open class Base {
    open val x: Int = 1
    open fun f() = println("Base Class f()")
}

class Child: Base() {
    override val x: Int = super.x + 1
    override fun f() = println("Child Class f()")
    
    inner class Inside {
        fun f() = println("Inside Class f()")
        fun test() {
            f()     // 현재 이너 클래스 f() 접근
            Child().f() // 바로 바깥 클래스 f()의 접근
            super@Child.f()     // Child의 상위 클래스인 Base 클래스의 f() 접근
            println("[Inside] super@Child.x: ${super@Child.x}") // Base의 x 접근
        }
    }
}

fun main() {
    val c1 = Child()
    c1.Inside().test()  // 이너 클래스 Inside의 메서드 test() 실행
}

이너 클래스에서 바깥 클래스 접근하기

 

package com.example.pp147

open class A {
    open fun f() = println("A Class f()")
    fun a() = println("A Class a()")
}

interface B {
    fun f() = println("B Interface f()")    // 인터페이스는 기본적으로 open임
    fun b() = println("B Interface b()")
}

class C : A(), B {  // 콤마,를 사용해 클래스와 인터페이스를 지정
    // 컴파일되려면 f()가 오버라이딩되어야 한다.
    override fun f() = println("C Class f()")
    fun test() {
        f() // 현재 클래스의 f()
        b() // 인터페이스 B의 b()
        super<A>.f()    // A 클래스의 f()
        super<B>.f()    // B 클래스의 f()
    }
}

fun main() {
    val c = C()
    c.test()
}

앵글브라켓을 사용한 이름 중복 해결

 

댓글