[Kotlin] Ch3-1. 추상 클래스와 인터페이스

leeeha·2022년 8월 4일
0

코틀린

목록 보기
19/29
post-thumbnail

출처: https://www.boostcourse.org/mo234/lecture/154291?isDesc=false

추상 클래스

  • 구현 클래스에서 가져야 할 명세를 정의한 클래스 (프로퍼티 및 메서드의 템플릿, 자식 클래스가 가져야 할 기본 뼈대)
  • abstract 키워드와 함께 선언하며 추상 클래스는 객체 생성 불가
  • 구체적이지 않은 것을 나타내기 때문에 자식 클래스에서 구체적으로 구현해줘야 함.
  • open 키워드를 사용하지 않고도 자식 클래스 생성 가능

ex) abstract class Vehicle ('탈 것'은 추상적이지만, 그 구체적인 예시로는 자동차, 오토바이, 배 등이 있다.)

package chap03.section1

abstract class Vehicle(val name: String, val color: String,
                       val weight: Double) {
    // 추상 프로퍼티 (하위 클래스에서 오버라이딩 필수)
    abstract val maxSpeed: Double
    // 비추상 프로퍼티
    var year: String = "2008"

    // 추상 메서드 (오버라이딩 필수) 
    abstract fun start()
    abstract fun stop()
    // 비추상 메서드 (구현부를 가짐)
    fun displaySpec(){
        println("name: $name, color: $color, weight: $weight, " +
                "year: $year, maxSpeed: $maxSpeed")
    }
}

class Car(name: String,
          color: String,
          weight: Double,
          override val maxSpeed: Double) : Vehicle(name, color, weight) {
    override fun start() {
        println("Car Started")
    }

    override fun stop() {
        println("Car Stopped")
    }

    fun autoPilotOn(){
        println("Auto Pilot ON")
    }
}

class Motorbike(name: String,
          color: String,
          weight: Double,
          override val maxSpeed: Double) : Vehicle(name, color, weight) {
    override fun start() {
        println("Motorbike Started")
    }

    override fun stop() {
        println("Motorbike Stopped")
    }
}

fun main() {
    //var v = Vehicle("Matiz", "red", 1000.0) // 인스턴스 생성 불가

    val car = Car("Matiz", "red", 1000.0, 100.0)
    val motor = Motorbike("Motor1", "yellow", 1000.0, 100.0)

    car.year = "2014"
    car.displaySpec()
    motor.displaySpec()

    car.start()
    motor.start()
}

단일 인스턴스로 객체 생성하기

object 키워드를 사용하면, 추상 클래스로부터 하위 클래스를 생성하지 않고도 단일 인스턴스로 객체를 생성할 수 있다.

abstract class Printer { // 추상 클래스
    abstract fun print() // 추상 메서드
}

// 하위 클래스 생성하지 않고 임시 객체 생성 가능! 
val myPrinter = object: Printer() {
    override fun print() {
        println("출력합니다.")
    }
}

fun main(){
    myPrinter.print()
}

인터페이스

인터페이스는 현실 세계에서 일종의 계약서와 같다. 계약서에는 무엇을 하라는 추상적인 활동들이 적혀있다. 이것을 어떤 '작업자'가 받아들였다면, 계약서에 있는 활동을 반드시 실행해야 한다. 계약서 자체로는 실행될 수 없다.

cf) 추상 클래스와의 차이점

  • 클래스를 다중 상속 받는 것은 불가능 하지만, 인터페이스는 클래스가 아니므로 여러 개를 구현할 수 있다.
  • 상위 클래스의 내용이 변경되더라도, 하위 클래스에 영향을 주지 않는다. (강한 연관을 갖지 않음.)

코틀린의 인터페이스

다른 언어와는 다르게, 인터페이스에 기본적인 구현 내용이 포함될 수 있다. (자바에서는 버전 8부터 default 키워드를 사용해야 내용을 구현할 수 있음.)

package chap03.section2

interface Pet {
    var category: String // abstract 키워드가 없어도 기본적으로 추상 프로퍼티
    fun feeding() // 추상 메서드
    fun patting(){ // 일반 메서드
        println("Keep patting!")
    }
}

class Cat(override var category: String) : Pet {
    override fun feeding() { // 오버라이딩 필수 
        println("Feed the cat a tuna can!")
    }
}

fun main() {
    val obj = Cat("small")
    println("Pet Category: ${obj.category}")
    obj.feeding() // 구현된 메서드 
    obj.patting() // 기본 메서드 
}

getter를 구현한 프로퍼티

인터페이스에서는 프로퍼티에 값을 저장할 수 없지만, val로 선언한 프로퍼티는 게터를 통해 필요한 내용을 구현할 수 있다.

package chap03.section2

interface Pet {
    var category: String // abstract 키워드가 없어도 기본적으로 추상 프로퍼티
    val msgTags: String
        get() = "I'm your lovely pet!"

    fun feeding() // 추상 메서드
    fun patting(){ // 일반 메서드
        println("Keep patting!")
    }
}

class Cat(override var category: String) : Pet {
    override fun feeding() { // 오버라이딩 필수
        println("Feed the cat a tuna can!")
    }
}

fun main() {
    val obj = Cat("small")
    println(obj.category)
    println(obj.msgTags)

    obj.feeding() // 구현된 메서드
    obj.patting() // 기본 메서드
}

여러 개의 인터페이스 구현

클래스는 기본적으로 다중 상속을 지원하지 않는다. 반면에, 인터페이스는 여러 개를 하나의 클래스에서 구현할 수 있으므로 다중 상속과 같은 효과를 가진다.

package chap03.section2

interface Bird {
    val wings: Int
    fun fly()
    fun jump() = println("bird jump!")
}

interface Horse {
    val maxSpeed: Int
    fun run()
    fun jump() = println("horse jump! $maxSpeed")
}

class Pegasus: Bird, Horse {
    override val wings: Int = 2
    override val maxSpeed: Int = 100
    override fun fly() = println("Fly!")
    override fun run() = println("Run!")
    override fun jump() {
        super<Bird>.jump()
        super<Horse>.jump()
        println("and Jump!")
    }
}

fun main() {
    val p = Pegasus()
    println("${p.wings} ${p.maxSpeed}")
    p.fly()
    p.run()
    p.jump()
}

인터페이스의 위임

package chap03.section2

interface A {
    fun functionA()
}

interface B {
    fun functionB()
}

class C(val a: A, val b: B){
    fun function(){
        a.functionA()
        b.functionB()
    }
}

class DelegatedC(a: A, b: B): A by a, B by b {
    // A와 B를 위임 받은 클래스는
    // 그들의 메서드를 자신의 것처럼 사용 가능
    fun functionC(){
        functionA() // A의 위임
        functionB() // B의 위임
    }
}
package chap03.section2

interface Nameable {
    var name: String
}

class StaffName: Nameable {
    override var name: String = "Sean"
}

class Work: Runnable {
    override fun run() {
        println("work...")
    }
}

class Person(name: Nameable, work: Runnable):
    Nameable by name, Runnable by work

fun main() {
    val person = Person(StaffName(), Work())

    // 위임 받은 인터페이스의 프로퍼티 및 메서드에 바로 접근
    println(person.name)
    person.run()
}
profile
습관이 될 때까지 📝

0개의 댓글