본문 바로가기
개발새발/코틀린

[코틀린/Kotlin] 상속과 인터페이스/추상클래스

by 조희우 2025. 8. 13.

상속

추상 클래스 상속

  • 상속을 받을 때는 extends가 아닌 :을 사용
  • 부모 클래스의 생성자를 바로 호출
  • kotlin의 클래스는 기본적으로 final: 상속 허용을 위해서는 open 키워드를 사용
    • 추상 멤버가 아니면 오버라이드 불가능
    • 생성자 또는 초기화 블럭에서 사용되는 프로퍼티는 open 사용을 피하기
  • 자식 클래스에서 메서드나 프로퍼티를 재정의하려면 override를 명시
  • 부모 클래스의 기능을 사용할 때는 super 사용
  • 정적 메소드가 없음
    • package-level 함수를 대신 사용
    • object, compoanion object 사용
open class Animal (
	protected val species: String,
	protected val legCount: Int,
){
	open fun sound() {
		println("동물이 소리를 냅니다.")
	}
}

class Dog(
	species: String: Animal(species, 4) {
    override fun sound() {
        println("멍멍!")
    }
}

인터페이스 상속

  • extdes가 아닌 :을 사용
  • default 키워드를 붙일 필요는 없음
  • 중복되는 인터페이스를 특정 시, super<타입>.함수
interface Flyable {
	fun act() {     // default 메서드
    	println("파닥 파닥")
    }
    
    fun fly()    // 추상 메서드
}
class Penguin(
	species: String 
) : Animal(species, 2), Swimable, Flyable {

	private val wingcount: Int = 2
    ...
    
    override fun act() {
    	super<Swimable>.act()   // Swimable.super.act()
        super<Flyable>.act()
    }
}

인터페이스

  • interface 키워드 사용
  • 접근 제어자는 public, open이며 접근 제어자 변경은 불가능
  • 구현(implementation)이 없는 함수만 가지거나, 기본 구현을 가짐
    • 클래스가 인터페이스를 상속받아 구현 → 클래스에서 인터페이스의 메서드들의 구현 부분을 채운 후 사용
  • 프로퍼티 선언이 가능
    • 인터페이스에서 초기화 불가능
    • 클래스에서 게터/세터 디폴트 구현을 가질 수 있으며 구현하지 않을 경우 디폴트 구현이 사용
    • filed 키워드 사용은 불가능
  • :을 사용하여 인터페이스 구현
    • 추상 메소드 뿐만 아니라 구현이 있는 메소드도 정의 가능
  • 다중 구현이 가능
    • 코틀린은 클래스의 다중 상속은 불가능하지만 인터페이스는 다중 상속이 가능
interface Movable {
    fun move()
}

interface Eatable {
    fun eat() {
        println("먹는다.")
    }
}

interface Playable {
	fun play()
	val playtype: String
	var playtime: Int
}

class Person : Movable, Eatable, Playable {
    override fun move() {
        println("사람이 움직인다.")
    }
    
    override val palytype: String = ""
    override val playtime: Int = 0
}

추상클래스

  • abstract 키워드를 사용하여 정의
  • 접근 제어자는 public, open이며 반드시 override
    • 추상메소드/프로퍼티가 아닌 일반 메서드/프로퍼티의 기본 접근 제어지는 public, final
  • 인스턴스화 불가능: 그 자체의 객체 생성 불가능
  • 추상 메서드와 일반 메서드 사용 가능
    • 추상 메서드가 하나라도 있다면 그 클래스는 추상 클래스
    • abstract 키워드로 추상 메서드 정의
    • 구현부(블럭)를 가질 수 없음
    • 추상 메서드는 자식 클래스에서 override하여 사용
  • 공통된 구현을 가질 수 있음: 일반 메서드도 포함 가능
  • 인터페이스와 달리 생성자나 상태(filed)를 가질 수 있음
  • 생성자를 가질 수 있음
    • concreate(비추상, 구상) 프로퍼티를 초기화
  • 클래스는 하나의 추상 클래스만 상속 가능
  • 추상 클래스도 추상 클래스 상속 가능
abstract class Animal {
    abstract fun makeSound()

    fun sleep() {
        println("잠을 잡니다")
    }
}

class Cat : Animal() {
    override fun makeSound() {
        println("야옹~")
    }
}

클래스, 추상 클래스, 인터페이스 비교

  클래스 추상클래스 인터페이스
키워드 class abastract class interface
상속(구현)해주는 일반적인 방법 class 하위 클래스 이름
: 상위 클래스 이름()
class 하위 클래스 이름
: 상위 추상 클래스 이름()
class 하위 클래스 이름
: 상위 인터페이스 이름
객체 생성 가능 불가능 불가능
기본 접근 제어자 public, final abastract poublic, open
(have to override)
public, open
(have orride)
(변경 불가)
concrete public, final
특정 클래스가 상속 받을 수 있는 최대 개수 1개 1개 1개 이상
디폴트 메서드 / 프로퍼티 - absract 구현 불가 구현부가 디폴트 / 
게터 세터 구현이 디폴트
concrete -

퀴즈

객관식

1. 상속과 다형성

다음 코드의 출력 결과는?

open class Parent {
    open fun hello() = println("Hello from Parent")
}

class Child : Parent() {
    override fun hello() = println("Hello from Child")
}

fun greet(p: Parent) {
    p.hello()
}

fun main() {
    val c = Child()
    greet(c)
}

  1. Hello from Parent
  2. Hello from Child
  3. 컴파일 오류
  4. 아무것도 출력되지 않음

답: 2

 

2. 인터페이스 구현

interface A {
    fun show() = println("A")
}

interface B {
    fun show() = println("B")
}

class C : A, B {
    override fun show() {
        super<A>.show()
        super<B>.show()
    }
}

fun main() {
    C().show()
}

  1. A
  2. B
  3. A B
  4. 컴파일 오류

답: 3

 

3. 추상 클래스와 구현

abstract class Animal {
    abstract fun sound(): String
}

class Cat : Animal() {
    override fun sound() = "Meow"
}

fun main() {
    val animal: Animal = Cat()
    println(animal.sound())
}

  1. 컴파일 오류
  2. Meow
  3. Cat
  4. 아무것도 출력되지 않음

답: 2

서술형

1. 상속과 override

다음 코드를 실행했을 때 출력 결과는?

open class Animal {
    open fun speak() {
        println("동물이 소리를 낸다")
    }
}

class Dog : Animal() {
    override fun speak() {
        println("멍멍")
    }
}

fun main() {
    val myDog: Animal = Dog()
    myDog.speak()
}

답: 멍멍

 

2. 인터페이스

interface Clickable {
    fun click()
}

interface Focusable {
    fun setFocus(b: Boolean)
}

class Button : Clickable, Focusable {
    override fun click() {
        println("클릭됨")
    }

    override fun setFocus(b: Boolean) {
        println("포커스 상태: $b")
    }
}

위 클래스 Button을 main 함수에서 사용하려면 어떻게 호출할까?

답: val btn = Button()

3. 추상 클래스

abstract class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double {
        return 3.14 * radius * radius
    }
}

Circle(3.0).area()의 결과는?

답: 28.26