BackEnd/Kotlin
10강. 코틀린에서 상속을 다루는 방법
hanseom
2024. 3. 4. 06:30
반응형
- 추상 클래스
- 인터페이스
- 클래스를 상속할 때 주의할 점
- 상속 관련 지시어 정리
추상 클래스
다음은 추상클래스인 JavaAnimal 코드입니다.
public abstract class JavaAnimal {
protected final String species;
protected final int legCount;
public JavaAnimal(String species, int legCount) {
this.species = species;
this.legCount = legCount;
}
abstract public void move();
public String getSpecies() {
return species;
}
public int getLegCount() {
return legCount;
}
}
Kotlin으로 구현하면 다음과 같습니다.
abstract class Animal(
protected val species: String,
protected val legCount: Int,
) {
abstract fun move()
}
다음은 JavaAnimal을 상속한 JavaCat 코드입니다.
public class JavaCat extends JavaAnimal {
public JavaCat(String species) {
super(species, 4);
}
@Override
public void move() {
System.out.println("고양이가 사뿐 사뿐 걸어가~");
}
}
Kotlin으로 구현하면 다음과 같습니다.
class Cat(
species: String
) : Animal(species, 4) { // 상위 클래스의 생성자 바로 호출
override fun move() {
println("고양이가 사뿐 사뿐 걸어가~")
}
}
- extends 키워드를 사용하지 않고 : 을 사용합니다. (Convention: 타입을 쓸 때는 변수명에서 한 칸 뛰지 않고 :을 붙이고, 상속에서는 한 칸 뛰고 :을 붙입니다.)
- 상속 시 상위 클래스의 생성자를 바로 호출합니다.
- override 지시어를 필수적으로 붙여 주어야 합니다.
다음은 JavaPenguin 클래스입니다.
public final class JavaPenguin extends JavaAnimal {
private final int wingCount;
public JavaPenguin(String species) {
super(species, 2);
this.wingCount = 2;
}
@Override
public void move() {
System.out.println("펭귄이 움직입니다~ 꿱꿱");
}
@Override
public int getLegCount() {
return super.legCount + this.wingCount;
}
}
Kotlin으로 구현하면 다음과 같습니다.
abstract class Animal(
protected val species: String,
protected open val legCount: Int, // override property
) {
abstract fun move()
}
class Penguin(
species: String
) : Animal(species, 2) {
private val wingCount: Int = 2
override fun move() {
println("펭귄이 움직입니다~ 꿱꿱")
}
override val legCount: Int
get() = super.legCount + this.wingCount
}
- 프로퍼티에 대한 override를 할 때(legCount), 추상 프로퍼티가 아니라면 open 키워드를 붙여줘야 합니다.
- 상위 클래스에 접근하는 키워드는 super입니다.
Java, Kotlin 모두 추상 클래스는 인스턴스화 할 수 없습니다.
인터페이스
다음은 JavaFlyable과 JavaSwimmable을 구현한 JavaPenguin 클래스입니다.
public interface JavaFlyable {
default void act() {
System.out.println("파닥 파닥");
}
}
public interface JavaSwimable {
default void act() {
System.out.println("어푸 어푸");
}
}
public final class JavaPenguin extends JavaAnimal implements JavaSwimable, JavaFlyable {
private final int wingCount;
... 생략 ...
@Override
public void act() {
JavaSwimable.super.act();
JavaFlyable.super.act();
}
}
Kotlin으로 구현하면 다음과 같습니다.
interface Swimable {
fun act() {
println("어푸 어푸")
}
}
interface Flyable {
fun act() {
println("파닥 파닥")
}
}
package com.lannstark.lec10
class Penguin(
species: String
) : Animal(species, 2), Swimable, Flyable {
private val wingCount: Int = 2
... 생략 ...
override fun act() {
super<Swimable>.act()
super<Flyable>.act()
}
}
- Kotlin의 인터페이스는 default 키워드 없이 메서드 구현이 가능합니다.
- Kotlin의 인터페이스도 추상 메서드를 만들 수 있습니다.
- Kotlin에서 인터페이스 구현도 :을 사용합니다.
- 중복되는 인터페이스를 특정할 때는 super<타입>.함수를 사용합니다.
Java, Kotlin 모두 인터페이스를 인스턴스화 할 수 없습니다.
Kotlin에서는 backing field가 없는 프로퍼티를 인터페이스에 만들 수 있습니다.
interface Swimable {
val swimAbility: Int
get() = 1
fun act() {
println(swimAbility)
println("어푸 어푸")
}
}
클래스를 상속받을 때 주의할 점
다음 코드의 수행 결과를 보면 출력되는 number의 값이 100도 아니고 300도 아닌 0입니다. 그 이유는, Base 클래스(상위 클래스)의 생성자가 실행되는 시점에 Derived 클래스(하위 클래스)의 number 값에 대한 초기화가 이루어지지 않은 상태이기 때문입니다.
그러므로, 상위 클래스를 설계할 때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야 합니다.
fun main() {
Derived(300)
}
open class Base(
open val number: Int = 100
) {
init {
println("Base Class")
println(number)
}
}
class Derived(
override val number: Int
) : Base(number) {
init {
println("Derived Class")
}
}
[수행 결과]
Base Class
0
Derived Class
상속 관련 지시어
- final: default로, override를 할 수 없게 합니다.
- open: override를 열어 줍니다.
- abstract: 반드시 override 해야 합니다.
- override: 상위 타입을 오버라이드 합니다.
감사합니다.
반응형