본문 바로가기
Kotlin/Kotlin 프로그래밍

[Kotlin] 다양한 클래스와 인터페이스 (2)

by 주 녕 2021. 5. 10.
728x90

모든 내용은 Do it! 코틀린 프로그래밍을 바탕으로 정리한 것입니다. 

 

데이터 클래스

특정 동작을 가지지 않고 오로지 데이터 저장을 위해 사용한다면 일반적인 클래스가 가지는 구현부가 필요 없음

→ 자원의 낭비를 막고자 오로지 데이터 저장에 초점을 맞춘 데이터 클래스 제공

 

데이터 전달을 위한 데이터 클래스

DTO(Data Transfer Object) : 데이터 전달을 위한 객체

POJO(Plain Old Java Object) : 자바에서 부르는 데이터 전달을 위한 객체

  • DTO는 구현 로직을 가지고 있지 않고 순수한 데이터 객체를 표현함
    • 속성과 속성을 접근하고자 하는 getter/setter를 가짐
    • 추가적으로 toString(), equals() 등과 같은 데이터를 표현하거나 비교하는 메서드를 가짐
  • DTO를 사용하는 이유?  DTO는 데이터를 주고받는 표준 방법이
    • 제어 로직을 위한 컨트롤러, 사용자와 상호작용을 위한 뷰, 데이터 표현을 위한 모델 등으로 나뉘는데 데이터를 주고 받는 일은 어디서든 자주 일어나는 일임
    • 표준과 같은 약속을 정하면 전송하거나 받고자 하는 어떤 요소든 데이터를 쉽게 다룰 수 있음

BUT, 코틀린에서는 내부적으로 자동 생성함

코틀린의 프로퍼티 = 필드(변수) + getter/setter

 

데이터 클래스 선언

    data class Customer(var name: String, var email: String)
  • data 키워드를 사용하여 선언
  • 주 생성자는 최소한 하나의 매개변수를 가져야 함
  • 주 생성자의 모든 매개변수는 val, var로 지정된 프로퍼티여야 함
  • 데이터 클래스는 abstract, open, sealed, inner 키워드를 사용할 수 없음 → 오로지 데이터를 기술하는 용도

 

데이터 클래스가 자동 생성하는 메서드

  • equals() : 두 객체의 내용이 같은지 비교하는 연산자
  • hashCode() : 객체를 구별하기 위한 고유한 정숫값 생성, 데이터 세트나 해시 테이블을 사용하기 위한 하나의 생성된 인덱
  • copy() : 빌더 없이 특정 프로퍼티만 변경해서 객체 복사하기
  • toString() : 데이터 객체를 읽기 편한 문자열로 반환하기
  • componentN() : 객체의 선언부 구조를 분해하기 위해 N번째 프로퍼티에 상응하는 메서드
    val cus1 = Custommer("J", "jun@mail.com")
    val cus2 = Custommer("J", "jun@mail.com")

    println(cus1 == cus2) // 연산자 == 은 내부적으로 equals()을 호출하는 것과 같음
    println(cus1.equals(cus2))
    
    // 객체를 구별하기 위한 고유한 정숫값을 생성
    // 만일 두 객체가 동등하다면 동일한 정숫값 생성
    println("${cus1.hashCode()}, ${cus2.hashCode()}") 
    
    val cus3 = cus1.copy(name = "Alice")
    println(cus1.toString()) // 객체를 읽기 쉽게 표현
    println(cus3.toString())

 

객체 디스트럭처링

디스트럭처링(Destructuring) : 객체가 가지고 있는 프로퍼티를 개별 변수로 분해하여 할당하는 것

    val (name, email) = cus1
    println("name = $name, email = $email")
    
    // 특정 프로퍼티가 필요 없는 경우
    val (_, email) = cus1
    
    // 개별적으로 프로퍼티를 가져올 수 있음
    val name2 = cus1.component1()
    val email2 = cus1.component2(
  • 변수를 선언할 때 소괄호를 사용하여 분해하고자 하는 객체를 지정함
  • 특정 프로퍼티가 필요 없는 경우, 언더스코어(_)를 사용하여 제외
  • componentN() 메서드를 사용하여 N번째 프로퍼티를 개별적으로 가져올 수 있음

 


내부 클래스 기법

클래스 내부에 또 다른 클래스를 설계하여 두는 이유는

  • 독립적인 클래스로 정의하기 모호한 경우나
  • 다른 클래스에서는 잘 사용하지 않지만 내부에서만 사용하고 외부에서는 접근할 필요가 없을 때가 있기 때문
  • 너무 남용하면 클래스의 의존성이 커지고 코드가 읽기 어려워짐
자바 코틀린
정적 클래스 (Static Class)
: static 키워드를 가지고, 외부 클래스를 인스턴스화하지 않고 바로 사용 가능한 내부 클래스
중첩 클래스 (Nested Class)
: 객체 생성 없이 사용 가능
멤버 클래스 (Member Class)
: 인스턴스 클래스로도 불리며 외 부 클래스의 필드나 메서드와 연동하는 내부 클래스
이너 클래스 (Inner Class)
: 필드나 메서드와 연동하는 내부 클래스로 inner 키워드가 필요함
지역 클래스 (Local Class)
: 초기화 블록이나 메서드 내의 블록에서만 유효한 클래스
 지역 클래스 (Local Class)
: 클래스의 선언이 블록 안에 있는 지역 클래스임
익명 클래스 (Anonymous Class)
: 이름이 없고 주로 일회용 객체를 인스턴스화하면서 오버라이드 메서드를 구현하는 내부 클래스. 가독성이 떨어지는 단점이 있음
익명 객체 (Anonymous Object)
: 이름이 없고 주로 일회용 객체를 사용하기 위해 object 키워드를 통해 선언됨

 

중첩 클래스

코틀린의 중첩 클래스는 기본적으로 정적(static) 클래스처럼 다뤄짐

→ 객체 생성 없이 접근 할 수 있음

  • 중첩 클래스의 메서드는 객체 생성 없이 호출될 수 있음
  • 중첩 클래스는 바로 바깥 클래스의 멤버에 접근할 수 없음

 

이너 클래스

inner 키워드 사용

단순히 내부에 작성된 중첩 클래스와는 다른역할을 함

  • 클래스 안에 이너 클래스를 정의할 수 있는데 이때 이너 클래스는 바깥 클래스의 멤버에 접근할 수 있음
  • private 멤버에도 접근이 가능함

 

지역 클래스

특정 메서드의 블록이나 init 블록과 같이 블록 범위에서만 유효한 클래스

→ 블록 범위를 벗어나면 사용되지 않음

  • 외부 프로퍼티 접근이 가능함

 

익명 객체

자바에서는 익명 이너 클래스라는 것을 제공하여 일회성으로 객체를 생성해 사용

코틀린에서는 object 키워드를 사용하는 익명 객체로 같은 기능을 수행

  • 다중 인터페이스를 구현할 수 있음
  • 인터페이스로부터 만들어진 객체는 이름이 없으며 일회성으로 사용됨
  • 호출될 때마다 일회성의 객체 인스턴스가 만들어지는 것

 


실드 클래스와 열거형 클래스

실드 클래스

실드(Sealed) : '봉인된' - 안전하게 보관하기 위해 묶어 두는 것

미리 만들어 놓은 자료형들을 묶어 제공하기 때문에 어떤 의미에서는 열거형(Enum) 클래스의 확장으로도 볼 수 있음

  • sealed 키워드를 class와 함께 사용
  • 그 자체는 추상 클래스와 같기 때문에 객체를 만들 수 없음
  • 생성자도 기본적으로는 private이며 private이 아닌 생성자는 허용하지 않음
  • 같은 파일 안에서는 상속이 가능하지만, 다르파일에서는 상속이 불가능
  • 블록 안에 선언되는 클래스는 상속이 필요한 경우 open 키워드로 선언될 수 있음
// 방법 1 ------------------------------------------------------
sealed class Result {
    open class Success(val message: String): Result()
    class Error(val code: Int, val message: String): Result()
}

class Status: Result()
class Inside: Result.Success("Status")

// 방법 2 ------------------------------------------------------
sealed class Result

open class Success(val message: String): Result()
class Error(val code: Int, val message: String): Result()

class Status: Result()
class Inside: Result.Success("Status")

 

열거형 클래스

여러 개의 상수를 선언하고 열거된 값을 조건에 따라 선택할 수 있는 특수한 클래스

  • 실드 클래스처럼 다양한 자료형을 다루지 못함
  • enum 키워드와 함께 선언할 수 있음
  • 자료형이 동일한 상수를 나열할 수 있음
enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

enum class DayOfWeek(val num: Int) {
    MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5)
    SATURDAY(6), SUNDAY(7)
}

val day = DayOfWeek.SATURDAY
when(day.num) {
    1, 2, 3, 4, 5 -> println("Weekday")
    6, 7 -> println("Weekend!")
}

enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0), ..., ORANGE(255, 165, 0);
    
    fun rgb() = (r*256+g)*256+b
}

fun main(args: Array) {
    println(Color.RED.rgb())
  • 각 상수의 값은 매개변수를 통해 초기화될 수 있음
  • 필요할 경우 메서드를 포함할 수 있는데 세미콜론(;)을 사용해 열거한 상수 객체를 구분함

 

애노테이션 클래스

애노테이션(Annotation) : 코드에 부가 정보를 추가하는 역할

@ 기호와 함께 나타내는 표기법으로 컴파일러나 프로그램 실행 시간에서 사전 처리를 위해 사용함

  • annotation 키워드를 사용해 클래스에 선언
  • 애노테이션 클래스를 선언하면 @를 붙여서 사용할 수 있음
  • 애노테이션은 속성을 사용하여 정의함
    • @Target : 애노테이션이 지정되어 사용할 종류(클래스, 함수, 프로퍼티 등)를 정의
    • @Retention : 애노테이션을 컴파일된 클래스 파일에 저장할 것인지 실행 시간에 반영할 것인지 결정
    • @Repeatable : 애노테이션을 같은 요소에 여러 번 사용 가능하게 할지를 결정
    • @MustBeDocumented : 애노테이션이 API의 일부분으로 문서화하기 위해 사용
  • 애노테이션은 클래스 앞, 메서드 앞, 프로퍼티 앞에 사용할 수 있음
  • 표준 애노테이션
    • @JvmName : filter()라는 이름을 자바에서 각각 filterStrings()와 filterInts()로 바꿔주는 것
    • @JvmStatic : 자바의 정적 메서드로 생성할 수 있게 해줌
    • @Throw : 코틀린의 throw 구문이 자바에서도 포함되도록 함
    • @JvmOverloads : 코틀린에서 기본값을 적용한 인자에 함수를 모두 오버로딩 해줌

애노테이션 클래스 제작은 프레임워크를 만들지 않는 이상 잘 사용하지 않는다고 하니 이쯤에서 그만하겠습니다 ;)

728x90

댓글