[Android / Kotlin] 인터페이스와 추상 클래스의 차이

영서·2023년 7월 9일
0

Kotlin Basics

목록 보기
4/5

1. 인터페이스

인터페이스와 추상 클래스는 객체 지향 프로그래밍에서 코드 재사용과 다형성을 구현하는 데 사용되는 개념이다. 둘을 비슷한 개념으로 볼 수 있지만, 인터페이스에서는 프로퍼티의 상태 정보를 저장할 수 없다.

1.1 인터페이스 특징

interface Car {
	var kind : String // 추상 프로퍼티
	fun code()  // 추상 메소드
	fun drive() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
		println("go to my home")
	}
}
  • 인터페이스는 interface 키워드를 사용하여 선언한다.
  • 인터페이스를 상속 받는 클래스에서 인터페이스를 통해 구현해야할 코드는 override 키워드를 앞에 넣어야한다.
  • 앞서 말한대로 프로퍼티에 상태를 저장할 수 없으므로(초기화 X) 기본 값을 가질 수 없다.
  • 단, 예외가 있다. val 로 선언한 프로퍼티는 게터를 통해서 필요한 내용 구현 가능.
  • 인스턴스화 할 수 없다.
interface Car {
    var name : String // 추상 프로퍼티
    val maxSpeed: Double // val로 선언하면 게터 구현 가능
        get() = 130.0
    fun navi()  // 추상 메소드
    fun drive() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
        println("go to my home")
    }
}

class Electronic(override var name : String) : Car { // 주생성자를 이용
    init {
        println("이름은 ${name}입니다.")
    }
    override fun navi() {
        println("navi 메소드가 구현되었습니다.")
    }
}

1.2 인터페이스의 장점

  • 코드가 간결해져 재사용성이 올라간다.
  • 클래스 간의 의존성을 제거할 수 있다.
  • 자바와 다르게 다중 상속이 되지 않지만, 인터페이스는 다중 상속이 가능하다.

두 개의 인터페이스를 통해 다중 상속을 구현한 예제

interface Ad {
    var melee: Double // val로 선언하면 게터 구현 가능
    fun offense_power()  // 추상 메소드
    fun attack() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
        println("으쌰")
    }
}
interface Ap {
    var ld: Double // val로 선언하면 게터 구현 가능
    fun spell_power() // 추상 메소드
    fun attack() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
        println("아브라카다브라")
    }
}
class Item: Ad, Ap { // 주생성자를 이용
    override var melee: Double = 1.0
    override var ld: Double = 40.0
    override fun offense_power() {
        println("offense_power 메소드")
    }

    override fun spell_power() {
        println("sppel_power 메소드")
    }

    override fun attack() {
        super<Ad>.attack()
    }
}

구현부가 있는 메소드의 경우 필요에 따라 오버라이딩을 하면 된다. 이때 인터페이스에서 구현한 메소드의 이름이 같은 경우에 super<인터페이스 이름>.메소드 명 형식으로 구분 가능하다.

2. 추상 클래스

추상메소드를 가지고 있는 클래스.

  • 인터페이스와 다르게 일반적인 프로퍼티 초기화 가능.
  • 인스턴스화 할 수 없다.
  • 추상 클래스를 정의하기 위해선 abstract 키워드 사용해야함.
  • 클래스 내에서의 프로퍼티나 메소드도 abstract 로 선언할 수 있다. -> 상속받는 클래스에서 구체화한다는 의미
  • 추상 클래스는 open 키워드 사용하지 않음.

예제 입력

// abstract로 정의한 추상 클래스이다. 주생성자를 사용했다.
abstract class Cat(val name : String, val color : String, val weight : Double) {
    // abstract로 정의한 추상 프로퍼티이므로 하위 클래스에서 반드시 재정의해야한다.
    abstract var sleep : Double

    // 초기값을 갖는 일반 프로퍼티 (인터페이스에서는 불가능)
    var birth = 2015

    // abstract로 정의한 추상 메소드이므로 하위 클래스에서 반드시 재정의해야한다.
    abstract fun cry()
    abstract fun eat()

    fun intro() {
        println("Name : $name, Color : $color, Weight : $weight, Sleep : $sleep, MaxSpeed : $birth")
    }
}

class RussianBlue(name : String, color : String, weight : Double, override var sleep : Double) : Cat(name, color, weight) {
    override fun cry() {
        // 재정의
        println("RussianBlue cry")
    }

    override fun eat() {
        // 재정의
        println("RussianBlue eat")
    }
}

class Siamese(name : String, color : String, weight : Double, override var sleep : Double) : Cat(name, color, weight) {
    override fun cry() {
        // 재정의
        println("Siamese cry")
    }

    override fun eat() {
        // 재정의
        println("Siamese eat")
    }
}

fun main() {
    // 추상 클래스
    val rb = RussianBlue("yom", "gray", 2.7, 13.37)
    val s = Siamese("mez", "beige", 3.5, 12.13)

    s.birth = 2019

    rb.eat()
    rb.intro()

    s.cry()
    s.intro()
}

출력

RussianBlue eat
Name : yom, Color : gray, Weight : 2.7, Sleep : 13.37, MaxSpeed : 2015
Siamese cry
Name : mez, Color : beige, Weight : 3.5, Sleep : 12.13, MaxSpeed : 2019

코드를 보면 s.birth 프로퍼티의 초기값을 변경한 것을 각각 intro() 메소드를 출력하여 확인할 수 있다.
결론적으로, 프로퍼티나 메소드에 abstract 키워드를 사용한 것은 하위 클래스에서 override 해야한다.

Reference

https://velog.io/@k906506/Kotlin-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4-VS-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4#%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4

profile
Let it rip

0개의 댓글