ABOUT ME

Developer를 위한 Reference 블로그 입니다.

Today
Yesterday
Total
  • 10강. 코틀린에서 상속을 다루는 방법
    BackEnd/Kotlin 2024. 3. 4. 06:30
    반응형
    1. 추상 클래스
    2. 인터페이스
    3. 클래스를 상속할 때 주의할 점
    4. 상속 관련 지시어 정리

     

    추상 클래스

      다음은 추상클래스인 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: 상위 타입을 오버라이드 합니다.

    감사합니다.

    반응형

    댓글

Designed by Tistory.