엔지니어 규의 IT 프로그래밍 다이어리

[Kotlin]10. 오버라이딩, 추상화, 인터페이스 본문

프로그래밍 언어/코틀린

[Kotlin]10. 오버라이딩, 추상화, 인터페이스

엔지니어 규 2022. 5. 11. 21:39
728x90

오버라이딩

 

상속시에는 기본적으로

 

Super class 에 있는 함수와 같은 이름과 형태를 가진  함수는 Sub class 에서는 만들수 없다.

 

하지만 Super class 에서 허용만 한다면 overriding 이라는 방법으로 subclass 에서

 

같은 이름과 형태로 된 함수의 내용을 다시 구현 할 수 있다.

 

아래의 소스코드를 보자.

fun main(){
 
    var a = Animal()
    var b = Tiger()
    a.eat()
    b.eat()
 
}
 
open class Animal{
 
    open fun eat(){
        println("음식을 먹는다.")
    }
 
}
 
class Tiger : Animal(){
 
    override fun eat() {
        println("고기를 먹는다.")
    }
 
}


음식을 먹는다.
고기를 먹는다.
cs

 

Super class 앞에 'open'이 붙은 함수는 서브클래스에서 'override'를 붙여 재구현하면 된다.

 

a.eat() 에서는 "음식을 먹는다." 가 출력되지만 b.eat() 에서는 "고기를 먹는다." 가 출력된다.

 

class Tiger : Animal(){

    override fun eat() {
        super.eat()
    }
}

만약에  eat  함수의 실행문에 super.eat() 을하면 b.eat() 에서 "음식을 먹는다" 가 출력된다.

 

super.함수 는 서브클래스의 함수가 슈퍼클래스의 함수 내용을 그대로 가져올때 사용한다.

 

 

이 사례에서는 이미 수퍼클래스에서 구현이 끝난 함수를 오버라이딩을 통해 재구현 하는 방법을 배웠다.

 

추상화

 

추상화는 선언부만 있고 기능이 구현되지 않은 추상함수와(abstract function),

 

추상함수를 포함하는 추상 클래스(abstract class)로 구성된다.

 

다음소스코드를 통해 추상함수를 포함한 추상클래스를 만들고, 서브클래스에서 상속받아 구현까지 하는 과정을 알아보자.

fun main(){
 
    var a = Rabbit()
    a.eat()
    a.sniff()
 
}
 
abstract class Animal{
    abstract fun eat()
    fun sniff(){
        println("킁킁거리다.")
    }
}
 
class Rabbit : Animal(){
    override fun eat(){
        println("당근을 먹는다.")
    }
 
}

당근을 먹는다.
킁킁거리다.
cs

 

추상 클래스를 생성하기 위해서는  [abstract class Animal] 처럼 앞에 abstract를 붙여준다.

 

추상 함수를 선언 할때도 마찬가지로 [abstract fun eat()] 처럼 앞에 abstract를 붙여준다.

 

이때 추상함수의 내용은 적지 않는다.    *추상함수는 비어있는 껍데기라고 생각하면 된다*

 

여기서 알수 있는 점은 추상 클래스안에 구현부가 없는 eat() 함수와, 구현부가 있는 sniff() 함수를 같이 쓸수 있다.

 

이런식으로 만든 추상 클래스는 일부 함수가 구현되지 않은 '미완성' 클래스 이기 때문에,

 

단독으로 인스턴스를 만들 수 없고 반드시 서브클래스에서 상속을 받아 abstract 표시가 된 함수를 구현해줘야 한다.

 

class Rabbit : Animal(){

    override fun eat(){
        println("당근을 먹는다.")
    }

 

그래서 Rabbit 이라는 클래스를 만들어 Animal을 상속받고, eat 이라는 추상함수의 실제 동작이  되는 구현부를 만들었다.

 

 

인터페이스

추상화를 하는 또 다른 방법으로 인터페이스 가 있다.

 

다른 언어를 배웠다면, 원래 인터페이스는 '추상함수로만 이루어져 있는 순수 추상화기능을 말하는것'  이라고 알고 있겠지만 

 

코틀린에서는 인터페이스가 속성, 추상함수, 일반함수 모두 가질 수 있다.

 

다만 추상함수는 생성자를 가질 수 있는 반면, 인터페이스는 생성자를 가질 수 없으며,

 

구현부가 있는 함수 -> open 함수로 간주

구현부가 없는 함수 -> abstract 함수로 간주

 

이런식으로 간주하기 때문에 별도로 'open' 이나 'abstract' 같은 키워드가 없어도 포함된 모든 함수를 

 

서브 클래스에서 구현 및 재정의가 가능하다. 

 

또한 한번에 여러 인터페이스를 상속받을 수 있으므로 좀더 유연한 설계가 가능하다.

 

아래의 소스코드를 보자.

fun main(){
 
    var d = Dog()
 
    d.run()
    d.eat()
 
}
 
interface Runner {
    fun run()
}
 
interface Eater {
    fun eat(){
        println("음식을 먹는다.")
    }
}
 
class Dog: Runner, Eater{
    override fun run(){
        println("열심히 뛴다.")
    }
 
    override fun eat() {
        println("허겁지겁 먹는다.")
    }
}


열심히 뛴다.
허겁지겁 먹는다.
cs
 

 

아래 코드 처럼 Runner 라는 interface 를 만들고 run 이라는 함수를 구현부 없이 만든다.

 

interface Runner {
    fun run()
}
cs

 

또한  Eater 라는 interface 를 만들고 eat 이라는 함수를 만든다.

 

interface Eater {
    fun eat(){
        println("음식을 먹는다.")
    }
}
cs
 

 

그리고 Dog 이라는 클래스로 두 인터페이스를 상속 받기위해  [class Dog: Runner, Eater] 처럼 

':' 을  클래스 이름 뒤에 붙이고 두 인터페이스를 ',' 를 붙여서 작성한다.

 

그리고 구현부가 없던 run 함수에 override 를 붙여 구현해주고, 구현부가 있던 eat 함수에도 override를 붙여 구현해준다.

 

이렇게하면 Dog 클래스는 두 인터페이스의 형식을 모두 물려받아 사용하는 서브클래스가 된다.

 

 

#여기서 주의해야 할 점은 

 

여러개의 인터페이스나 클래스에서 같은 이름과 형태를 가진 함수를 가진 형태를 구현하고 있다면

 

서브클래스에서는 혼선이 일어나지 않도록 반드시 오버라이딩하여 재구현해야 한다.

 

 

 

 

정리하자면 오버라이딩(overriding)이미 구현이 끝난 함수의 기능을 서브클래스에서 변경해야 할 때,

 

그리고 추상화(abstract)형식만 선언하고 실제 구현은 서브클래스에 일임할때 사용하는 기능이며,

 

인터페이스(interface)서로 다른 기능들을 여러개 상속해야 할 때 유용한 기능이다. 

728x90
Comments