본문 바로가기
Language/Kotiln

[Kotlin] 코틀린 심화문법3 (data class와 open class)

by 카프리썬 2022. 2. 3.
728x90

지금까지 코틀린 기본세팅을 진행했다.

2022.01.29 - [Kotlin] 코틀린 기본개념 - 코틀린이란? 도대체 왜 쓰는가? 특징!

2022.01.28 - [Kotlin] 코틀린 환경설정 - Intellj에서 코틀린 프로젝트 생성하기

2022.01.29 - [Kotiln] Intellj에서 코틀린 코딩컨벤션 Kotlin Style Guide 적용하기

2022.01.30 - [Kotiln] 코틀린 실행하기 - Scratch File (스크래치 파일) 만들기

 

그리고 간단한 기본문법도 살펴봤다. 

2022.01.29 - [Kotiln] 코틀린 기본문법1 (변수선언/함수선언/자료형/반복문/조건문)

2022.01.30 - [Kotiln] 코틀린 기본문법2 (NULL처리 : lateinit/lazy/!!/?/?.)

2022.01.30 - [Kotiln] 코틀린 기본문법3 (클래스와 컬렉션: List/Map/Set)

 

약간 심화문법의 기준을 어디까지 나눠야할지 감이 안잡혀서 실제 코드를 보면서 낯선 개념 위주로 정리해볼 생각이다.

2022.01.31 - [Kotiln] 코틀린 심화문법1 (람다식:lambda expression)

2022.01.31 - [Kotiln] 코틀린 심화문법2 (확장함수/고차함수/표준라이브러리)

 

728x90

Data Class(데이터클래스)

데이터 보관 목적으로 만든 클래스로, 일반 클래스와 달리 다양한 메소드를 자동으로 생성해준다. 

프로퍼티에 대한 아래와 같은 메소드들이 자동으로 생성된다. 

  • hashCode()
  • copy()
  • equals()
  • toString()
  • componentsN()

데이터 클래스는 아래와 같은 특징을 가지고 있다. 

  • 기본 생성자에 1개 이상의 파라미터가 있어야 함
  • 기본 생성자의 파라미터가 val 또는 var 로 선언해야 함
  • 다른 클래스를 상속받을 수 없음 (슈퍼 클래스를 가질 수 없음)
    단, sealed 클래스는 상속받을 수 있으며, 인터페이스는 구현할 수 있음 (v1.1 이후 기준)
  • abstract, open, sealed, inner 등 키워드를 붙일 수 없음
  • 자동으로 생성한 메소드를 오버라이딩할 경우, 오버라이드 된 메소드 사용

만약 Data Class(데이터클래스)가 없었다면..?

자동으로 생성되지 않았다면 아래처럼 직접 toString()을 오버라이딩하고, getter/setter를 정의해줘야했다.

예를 들어 이름과 나이를 같는 people이라는 클래스를 정의한다고 하면 아래처럼 미리 보일러플레이트 코드가 필요했다. 

class People {
    String name;
    int age;

    @Override
    public String toString(){
        return "[People] name : " + name + ", age : " + Integer.toString(age);
    }

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }
}

위와 같은 내용을 data class로 간소화할 수 있다.

 

데이터클래스 선언

마찬가지로 이름과 나이를 같는 people이라는 클래스를 정의한다고 하면 클래스 앞에 data 키워드를 붙여서 data class를 생성한다.

data class People(
    val name: String,
    val age: Int
)

단지 이게 전부다. 이렇게 생성을 하고나면 데이터 클래스 객체를 생성해서 사용하면 된다. 

fun main() {
    val peopleA = People("H43RO", 23)
    val peopleB = People("LULU", 21)
}

 

그럼 자동으로 hashCode(), copy(), equals(), toString(), componentsN() 함수들이 구현되어 있어서 호출할 수 있다.

 

toString() 메소드

생성한 객체를 그대로 출력하면 프로퍼티의 값이 알아서 출력된다. 

디버깅시 로그캣을 가지고 매우편리하게 사용할 수 있다.  

println(peopleA)

// People(name=H43RO, age=23)

copy() 메소드

특정 필드값만 바꿔서 복사할 수 있다.

val olderPeopleA = peopleA.copy(age = 33)
println(olderPeopleA)

// People(name=H43RO, age=33) -> 나이가 23 에서 33 으로 변경되었음!

hashCode() 메소드

프로퍼티가 같은 객체를 만들고, 각각의 hashcode를 출력하면 그 값이 같다. 

val peopleA = People("H43RO", 23)
val peopleB = People("H43RO", 23)

println(peopleA.hashCode())
println(peopleB.hashCode())

equals() 메소드

동등한 값을 담고 있는지 검사할 수 있다. (==)

println(peopleA == peopleB)

// true (두 객체의 프로퍼티가 완전히 같음)

메모리상 같은 객체를 가지고 있는지 검사할 수 있다. (===)

println(peopleA === peopleB)

// false (두 객체의 메모리 주소가 다름)

componentN() 메소드

각 프로퍼티에 번호가 붙어서 구조분해가 가능한 형태가 된다.

 그래서 프로퍼티의 값을 가독성있게 분해해서 각각 값을 쓸 수 있다.

val peopleA = People("H43RO", 23)
val (name, age) = peopleA

println("Destructuring Declarations : $name, $age")

// Destructuring Declarations : H43RO, 23

 

이 클래스는 프로퍼티가 2개이다.

이 때 선언 순서가 name 다음에 age 형태로 되어있기 때문에, 

component1() 메소드에 name 필드가 대응되고, component2() 메소드에 age 필드가 대응되게 된다.

이 모든 과정을 컴파일러가 알아서 해준다!  실제로 . 을 찍어서 멤버를 확인해보면, 메소드가 생성되어 있다.

 

반응형

Open Class(데이터클래스)

결론부터 말하면, open class는 상속을 하기 위한 키워드이다. 

 

자바에서는 기본적으로 상속이 허용된다. 그래서 명시적으로 final 키워드를 사용해야만 상속이 불가능하다. 

하지만 반대로 코틀린에서는 기본적으로 상속이 허용되지 않는다. 그래서 명시적으로 open 키워드를 사용해야만 상속이 가능하다

 

예를 들면 아래와 같이 사용할 수 있다. 

open class RichButton : Clickable {
  fun disable() { /* ... */ } 
  // final 함수 : 하위 클래스가 오버라이드 불가
  open fun animate() { /* ... */ } 
  // open 함수 : 하위 클래스가 오버라이드 가능
  
  override fun click() { /* ... */ } 
  // 상위 클래스에서 선언된 open 함수를 오버라이드 한 것. 
  // 오버라이드한 메소드는 기본적으로 open
  // 오버라이드한 메소드의 구현을 하위 클래스에서 못하게 하려면 앞에 final을 명시해야한다.
}
open class RichButton : Clickable {
  final override fun click() { /* ... */ } 
  // open class가 override한 메소드는 기본적으로 open
  // 그런데 다시 하위 메소드에서 다시 쓰이지 못하려고 final명시
}

 

코틀린의 기본값을 final로 보면되고, 메소드나 변수 앞에 open을 붙여야 해당값은 오버라이딩이나 상속이 가능해진다.

오버라이딩한 함수자체는 oepn이고, 그 오버라이딩한 메소드를 다시 하위클래스에서 구현하지 못하게 하려면 final을 명시한다. 

 

open class Car {

    // 이 메서드는 하위 클래스에서 override 불가능
    fun getNumberOfTires(): Int {
        return 4
    }
    
    // 하위 클래스에서 override 가능
    open fun hasSunRoof() :Boolean {
        return false
    }
}

// open 클래스는 상속이 가능하다!
class Benz() : Car() {

    // getNumberOfTires 메서드는 override 불가능
    // hasSunRoof 메서드는 open변경자가 붙어서 override 가능
    override fun hasSunRoof(): Boolean {
        return true
    }
}

 

그래서 open class는 다른 클래스에서 상속할 수 있고, open method는 하위클래스에서만 오버라이딩 할 수 있다. 

 

 

 

참고

data class 알아보기 

data class 구현예시 

 

kotiln키워드 정리

코틀린 클래스

 

 

반응형