코틀린 상속과 인터페이스

상속과 인터페이스

코틀린은 객체지향 언어답게 상속과 인터페이스를 통한 코드 재사용과 확장이 매우 강력하다.
이 글에서는 클래스 상속, 인터페이스 다중 구현, 오버라이딩, super 키워드 활용법까지 모두 다룬다.


클래스 상속 기본 구조

코틀린의 클래스는 기본적으로 final이다.
즉, 상속을 허용하려면 반드시 open 키워드를 붙여야 한다.

open class Parent {
open fun greet() {
println("부모 클래스 인사")
}
}
class Child : Parent() {
override fun greet() {
println(“자식 클래스 인사”)
}
}

실행 예시

val child = Child()
child.greet() // 출력: 자식 클래스 인사

super 키워드

super를 사용하면 부모 클래스의 속성이나 메서드를 참조할 수 있다.

open class Animal {
open fun sound() {
println("동물의 소리")
}
}
class Dog : Animal() {
override fun sound() {
super.sound()
println(“멍멍!”)
}
}

생성자 상속

부모 클래스에 생성자가 있을 경우, 자식 클래스는 반드시 호출해야 한다.

open class Person(val name: String)

class Student(name: String, val studentId: Int) : Person(name)


속성 오버라이드

프로퍼티 또한 openoverride 키워드를 사용해 재정의할 수 있다.

open class Car {
open val brand: String = "Generic"
}
class Tesla : Car() {
override val brand: String = “Tesla”
}

추상 클래스 (Abstract Class)

abstract 키워드는 구현이 불완전한 클래스를 정의할 때 사용한다.
추상 클래스는 인스턴스화할 수 없으며, 반드시 상속을 통해 완성해야 한다.

abstract class Shape {
abstract fun area(): Double
}
class Circle(val r: Double) : Shape() {
override fun area(): Double = 3.14 * r * r
}

예시 실행

val circle = Circle(3.0)
println(circle.area()) // 출력: 28.26

인터페이스 기본 구조

인터페이스는 클래스에 특정 기능을 “약속”하는 역할을 한다.
interface 키워드를 사용하며, 여러 인터페이스를 동시에 구현할 수 있다.

interface Drivable {
fun drive()
}
interface Maintainable {
fun repair()
}class Car : Drivable, Maintainable {
override fun drive() {
println(“운전 중…”)
}override fun repair() {
println(“정비 중…”)
}
}


인터페이스의 기본 구현

인터페이스에서도 기본 구현(default implementation) 을 가질 수 있다.

interface Machine {
fun start() {
println("기계가 작동합니다.")
}
}
class Robot : Machineval r = Robot()
r.start() // 출력: 기계가 작동합니다.

인터페이스 다중 상속 충돌 해결

두 인터페이스에 같은 함수가 정의된 경우,
구현 클래스에서 super<인터페이스명>으로 명시적으로 지정해야 한다.

interface A {
fun hello() = println("A의 인사")
}
interface B {
fun hello() = println(“B의 인사”)
}class C : A, B {
override fun hello() {
super<A>.hello() // A 쪽 호출
super<B>.hello() // B 쪽 호출
}
}

인터페이스 속성

인터페이스에서도 속성을 정의할 수 있지만, 상태는 가질 수 없다.
즉, 값은 구현 클래스에서 초기화해야 한다.

interface Identifiable {
val id: String
}
class User(override val id: String) : Identifiable

상속과 인터페이스 함께 사용

코틀린은 단일 상속만 가능하지만, 여러 인터페이스를 동시에 구현할 수 있다.

open class Device(val name: String) {
open fun info() {
println("기기 이름: $name")
}
}
interface Powerable {
fun powerOn() = println(“전원이 켜졌습니다.”)
}class Smartphone(name: String) : Device(name), Powerable {
fun boot() {
powerOn()
info()
}
}

sealed 클래스

sealed 클래스는 상속 가능한 클래스를 제한할 때 사용한다.
같은 파일 내에서만 상속이 가능하며, 주로 when과 함께 사용한다.

sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
fun handle(result: Result) {
when (result) {
is Success -> println(“성공: ${result.data}“)
is Error -> println(“실패: ${result.message}“)
}
}

클래스와 인터페이스 비교표

구분 키워드 인스턴스 생성 다중 상속 구현 여부
클래스 class / open class 가능 불가 구현 포함
추상 클래스 abstract class 불가 1개만 일부 구현
인터페이스 interface 불가 가능 구현 선택적

  • 공통 로직은 추상 클래스,
    행동 규약은 인터페이스로 분리하면 깔끔하다.

  • sealed class는 결과 타입이나 상태 관리(Success, Error, Loading)에 자주 사용된다.

  • 다중 인터페이스 충돌은 super<인터페이스>로 해결한다.

  • 상속 구조가 깊어질수록 유지보수가 어려워지므로, 조합(Composition) 패턴도 고려하자.



게시됨

카테고리

작성자

댓글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다