ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin] 클래스, 상속, 접근제어, object
    Backend/코틀린 2023. 3. 20. 17:10
    반응형

    출처 - 인프런 자바개발자를 위한 코틀린 입문 강의 ( 9~14강 )

    https://www.inflearn.com/course/java-to-kotlin

    CH9. 클래스를 다루는 방법

    1. 코틀린에서는 필드를 만들면 getter와 setter를 자동으로 만든다.
      1. 필드 하나를 프로퍼티라고 부름
    2. 코틀린에서는 주 생성자가 필수임
    3. custom getter, setter를 만들 수 있음

    생성자

    class Person {
      val name: String,
      var age: Int,
    } {
      init { // 가장먼저호출
        if (age < 0) {
          throw new Exception()
        }
      }
      constructor(name: String): this(name, 1){
        println("첫번째 부 생성자")
      }
    }

    부생성자보다는 default parameter 또는 정적팩토리메서드를 권장함

    class Person {
      val name: String = "홍길동",
    }

    custom getter

    class Person {
      val name: String,
      var age: Int,
    } {
      
      // 원래함수형태
      fun isAdult(): Boolean {
        return this.age >= 20
      }
      
      // Custom getter 
      val isAdult: Boolean {
        get() = this.age >= 20
      }
    }
    • 객체의 속성을 나타내는것 : custom getter
    • 아니면 함수형태

    예) 이름 대문자로 바꾸는 예제

    class Person (
      name: String = "jisu",
    ) {
      val name = name
        get() = field.uppercase()
      //get() = name.uppsercase() 이렇게쓰면 무한루프 발생돼서 자기자신 가리키는 필드를 의미하는 field 예약어 씀 (backing field)
    }
    class Person (
      name: String = "jisu",
    ) {
      val uppercaseName: String 
        get() = this.name.uppercase()
    }

    backing field말고 this.name 으로 써도 됨

    custom setter

    setter 자체는 지향하기때문에 custom setter는 많이 안 씀

    class Person (
      name: String = "jisu",
    ) {
      var name = name
        set(value) {
          field = value.uppercase()
        }
    }

     


     

    CH10. 상속을 다루는 방법

    1. 상속 또는 구현을 할 때 : 사용
    2. 상위 클래스 상속을 구현할 때 생성자를 반드시 호출해야 한다
    3. override를 필수로 붙여야 한다
    4. 추상멤버가 아니면 기본적으로 오버라이드가 불가능 -> open 사용

    추상클래스

    abstract class Animal(
      protected val species: String,
      protected open val legCount: Int, // 프로퍼티 오버라이드할 때 open 붙여주어야함
    ) {
      abstract fun move()
    }
    class Cat (
      species: String
    ) : Animal(species, 4){ // :로 상속받으면서 바로 super를 불러줌
      
      override fun move() { // 어노테이션 대신 override
        println("고양이")
      }
    }
    ​
    class Penguin(
      val species: String
    ) : Animal(species, 2) {
      private val wingCount: Int = 2
      
      override fun move() {
        println("펭귄")
      }
      
      override val legCount: Int
        get() = super.legCount + this.wingCount
    }

    java의 extends 대신 : 를 사용해서 상속받음

    • 변수 타입은 species: String한 칸 안띄고 : 씀
    • 상속받을 때는 Cat () : Animal 한칸 띄고 씀

    open 키워드

    • 프로퍼티 오버라이드 할 때 open을 붙여줘야 함

    인터페이스

    interface Flyable {
      fun act() { // default 쓰지 않아도 java default함수처럼 됨
        println("파닥파닥")
      }
      
      fun fly() // 추상메서드
    }
    ​
    interface Swimable {
      println("어푸 어푸")
    }
    class Penguin(
      val species: String
    ) : Animal(species, 2), Swimable, Flyable {// 그냥 :로 인터페이스도 구현 
      private val wingCount: Int = 2
      
      override fun move() {
        println("펭귄")
      }
      
      override val legCount: Int
        get() = super.legCount + this.wingCount
      
      override fun act() {
        super<Swimable>.act() // 특정 상위 인터페이스 함수 오버라이드할 때 꺽쇠로 표현
        super<Flyable>.act()
      }
    }
    • 구현은 :
    • 중복 인터페이스 특정할 때는 super<타입>

    클래스 상속할 때 주의할 점

    • 코틀린은 클래스를 상속받으려면 open 키워드를 써줘야 함. 기본은 불가능
    • 상위 클래스를 설계할 때 생성자 또는 초기화블록에 사용되는 프로퍼티에는 open을 피해야 한다.

    상속 관련 키워드

    • final : override 할 수 없게 함. default값
    • open : override를 열어준다
    • abstract: override 강제
    • override: 상위 타입을 오버라고 하고 있다는 의미로 사용하는 키워드

     


     

    CH11. 접근제어를 다루는 방법

    가시성 제어

    javakotlin

    public 모든 곳에서 접근 가능 모든 곳에서 접근 가능
    protected 같은 패키지 또는 하위클래스 선언된 클래스 또는 하위클래스
    default 같은 패키지에서만 사용 가능  
    internal   같은 모듈에서만 접근 가능
    private 선언된 클래스 내에서만 가능 선언된 클래스에서만 접근 가능
    • 코틀린에서는 패키지를 가시성제어에는 사용하지 않음 -> 모듈
    • kotlin의 기본 접근지시 : public ( java는 default )

    파일의 접근제어(. kt)

    • 파일에 변수, 함수, 클래스 여러 개 만들 수 있음
    • protected는 클래스단위로 쓰이는 거라 파일에서는 사용할 수 없음

    기타

    • 생성자에 접근지시어를 붙이려면 생략된 constructor를 명시해서 써야 함
      • class Cat internal constructor
    • 파일최상단에 .kt 유틸용 파일을 만들면 편하다
      • 자바에서 static 메서드 + private생성자 만들어 쓴 거랑 비슷
    • 프로퍼티 가시성은 생성자에 지시어를 붙이거나, custom getter, setter 만들면서 get, set에다가 만 붙여줄 수 있음
    • 자바와 코틀린이 protected, internal이 의미가 달라서 같이 쓸 때 주의해야 함

     


     

    CH12. object 키워드

    static 함수와 변수

    class Person private constructor (
      var name: String,
      var age: Int,
    ) {
      companion object Factory : Log { // Log 인터페이스 구현
        private const val MIN_AGE = 0 // const: 진짜 상수
        
        //@JvmStatic
        fun newBaby(name: String): Person {
          return Person(name, MIN_AGE)
        }
        
        override fun log() {
          println("Person 클래스 동행객체 Factory")
        }
      }
    }
    ​
    interface Log {
      fun log()
    }
    • 코틀린은 static 키워드 대신 companion object 사용
      • static : 클래스가 인스턴스화될 때 새로운 값이 복제되는 게 아니라 정적으로 인스턴스끼리의 값 공유
      • companion object: 클래스와 동행하는 유일한 오브젝트
    • val과
      • val : 런타임시 할당
      • const val : 컴파일 시 할당, 진짜 상수. 기본값과 String에만 붙일 수 있다
    • companion object는 하나의 객체로 간주되어 인터페이스 구현, 이름 짓기 모두 가능
    • 자바에서 코틀린의 companion을 사용하고 싶을 때
      • 이름이 없을 때
        • Person.Companion.newBaby("ABC")
      • @JvmStatic 어노테이션 붙여줬을 때
        • Person.newBaby("ABC")
      • 이름 있을 때
        • Person.Factory.newBaby("ABC")

    싱글톤 : 단 하나의 인스턴스만 갖는 클래스

    자바의 싱글톤

    public class JavaSingleton {
      private static final JavaSingleton INSTANCE = new JavaSingleton();
      private JavaSingleton() {  }
      public static JavaSingleton getInstance() {
        return INSTANCE;
      }
    }

    코틀린의 싱글톤 -> object만 붙여주면 됨

    //생성
    object Singleton {
      var a: Int = 0
    }
    ​
    //사용
    fun main() {
      println(Singleton.a)
    }

    익명 클래스

    특정 인터페이스 클래스를 상속받은 구현체를 일회성으로 사용할 때 파라미터에 object : 타입이름 써서 구현

    fun main() {
      moveSomething(object : Movable {
        override fun move() {
          println("무브")
        }
        override fun fly() {
          println("플라이")
        }
      })
    }
    ​
    private fun moveSomthing(movable: Movable) {
      movable.move()
      movable.fly()
    }

     


     

    CH13. 중첩 클래스

    중첩 클래스 종류 (java 기준)

    static을 사용하는 중첩클래스 : 코틀린 기본(권장)

    • 클래스 안에 static을 붙인 클래스는 바깥 클래스 직접 참조 불가
    • class House(
        private val address: String,
        private val livingRoom: LivingRoom
      ) {
        class LivingRoom(
          private val area: Double
        )
      }

    static을 사용하지 않는 중첩클래스

    • inner class : 코틀린에서 inner 키워드 붙여줘야함
      • 바깥 클래스 참조 가능 this.@바깥클래스
      • class House(
          var address: String,
        ) {
          var livingRoom = this.LivingRoom(10.0)
          
          inner class LivingRoom(
            private val area: Double
          ) {
            val address: String
              get() = this@House.name // 바깥클래스 참조
          }
        }
    • local class
    • anonymous class
    class House(
      private val address: String,
      private val livingRoom: LivingRoom
    ) {
      class LivingRoom(
        private val area: Double
      )
    }
    class House(
      private val address: String,
      private val livingRoom: LivingRoom
    ) {
      class LivingRoom(
        private val area: Double
      )
    }

     


     

    CH14. 다양한 클래스 다루는 방법

    Data Class

    • getter, setter
    • equals and hashCode
    • toString
    data class PersonDTO(
      val name: String,
      val age: Int,
    )
    // 자동으로 다 만들어줌
    • java에서도 jdk16부터 비슷한 record class 도입했음

    Enum Class

    • 특징 : 상속 X , 인터페이스 구현O, 싱글톤
    • kotlin에서 when 으로 분기처리를 쉽게 할 수 있음
      • else 사용 X
      • enum 변화 체크
    enum class Country (
      private val code: String,
    ) {
      KOREA("KO"),
      AMERICA("US")
      ;
    }
    ​
    ​
    ​
    fun handleCountry(country: Country) {
      when (country) {
        Country.KOREA -> TODO()
        Country.AMERICA -> TODO()
        // else 를 작성 할 필요가 없음
        // enum에 다른 값이 추가되면 경고를 줌
      }
    }

    Sealed Class, Sealed Interface

    • 등장배경 ) 상속이 가능하도록 추상클래스를 만들지만 외부에서는 이 클래스를 상속받지 않았으면 좋겠음
    • 컴파일 타임 때 하위 클래스의 타입을 모두 기억해서 런타임 때 클래스 타입이 추가될 수 없음
    • 하위 클래스는 같은 패키지에 있어야 함
    • Enum과 다른 점
      • 클래스 상속 O
      • 하위 클래스 멀티 인스턴스 가능
    • 추상화가 필요한 Entity나 DTO에서 사용하고 JDK17에서도 sealed Class가 추가되었음
    /// 선언 
    sealed class HyundaiCar(
      val name: String,
      val price: Long
    )
    ​
    // 같은 패키지 또는 같은 파일에 구현되어야함 
    class Avante : HyundaiCar(~~)
    class Sonata : HyundaiCar(~~)
    class Grandeur : HyundaiCar(~~)
    ​
    ​
    /// 사용
    fun main() {
      handleCar(Avante())
    }
    ​
    private fun handleCar(car: HyundaiCar) {
      when (car) {
        is Avante -> TODO()
        is Grandeur -> TODO()
        is Sonata -> TODO()
      }
    }

    'Backend > 코틀린' 카테고리의 다른 글

    [Kotlin] Collection, 함수, 람다, 기타 ..  (0) 2023.03.26

    댓글

Designed by Tistory.