다양한 클래스와 인터페이스 (2/2)

장똑대·2022년 4월 13일
0

Do it! 코틀린 프로그래밍 [둘째마당, 객체 지향 프로그래밍] 학습

✏️2. 데이터 클래스와 기타 클래스✏️


2-1. 데이터 클래스

자원의 낭비를 막고 오로지 데이터 저장에 초점을 맞추기 위해 코틀린에서 특별히 고안된 클래스

📌 DTO(Data Transfer Object)

  • 데이터 전달을 위한 객체
  • 구현 로직을 가지고 있지 않고 순수한 데이터 객체를 표현하기 때문에 보통 속성과 게터/세터를 가짐
  • 표준과 같은 약속을 정해놓고 데이터를 쉽게 다루기 위해 사용

📌 데이터 클래스에서 내부적으로 자동 생성되는 메서드

  • 게터/세터
  • 비교를 위한 equals()와 키 사용을 위한 hashCode()
  • 프로퍼티를 문자열로 변환해 순서대로 보여주는 toString()
  • 객체 복사를 위한 copy()
  • 프로퍼티에 상응하는 component1(), component2()

📌 데이터 클래스 선언과 조건

  • data class Customer(var name: String, var email: String)
  • 데이터 클래스의 조건
    • 주 새엉자는 최소한 하나의 매개변수를 가져야 함
    • 주 생성자의 모든 매개변수는 val, var로 지정된 프로퍼티여야 함
    • 데이터 클래스는 abstract open sealed inner 키워드 사용 불가
  • 부 생성자나 init 블록을 넣어 데이터를 위한 간단한 로직을 포함할 수 있음

📌 객체 디스트럭처링

  • 디스트럭처링(Distructuring) : 객체가 가지고 있는 프로퍼티를 개별 변수로 분해하여 할당하는 것
data class Customer(var name: String, var email: String) { }

fun main(){
    val customer1 =  Customer("Sean", "sean@mail.com")
    val (name, email) = customer1
    
    // name 프로퍼티가 필요 없는 경우
    val (_, email) = customer1
    
    // 개별적으로 프로퍼티를 가져오기
    val name = customer1.component1()
    val email = customer1.component2()
}

-> customer1 객체의 프로퍼티 값 2개를 각각 name과 email로 선언된 변수로 가져옴
-> 가져올 필요가 없는 프로퍼티는 언더스코어( _ )를 사용해 제외
-> 개별적으로 프로퍼티를 가져오기 위해 componentN() 메서드를 사용

⬇️ 람다식을 사용해 디스트럭처링된 변수 출력하기

val myLamda = {
	(nameLa, emailLa): Customer ->
    println(nameLa)
    println(emailLa)
}

myLambda(customer1)

2-1. 내부 클래스 기법

  • 독립적인 클래스로 정의하기 모호한 경우나 내부에서만 사용하고 외부에서는 접근할 필요가 없을 때 클래스 내부에 또 다른 클래스를 설계
  • 내부 클래스를 남용하게 되면 클래스의 의존성이 커지고 코드가 읽기 어렵게 됨
// 자바의 멤버(이너) 클래스
class A {
	class B { /* 외부 클래스 A의 필드에 접근 가능 */ }
}

// 코틀린의 이너 클래스
class A {
	inner class B { /* inner 키워드 필요, 외부 클래스 A의 필드에 접근 가능 */}
}


// 자바의 정적 클래스
class A {
	static class B { /* 정적 클래스를 위해 static 키워드 사용 */ }
}

// 정접 클래스러처럼 사용한 코틀린의 중첩 클래스
class A {
	class B { /* 아무 키워드가 없으면 중첩 클래스 */ 
    /* 외부 클래스 A의 프로퍼티, 메서드에 접근할 수 없음 */
    }
}

📌 중첩 클래스 (Nested Class)

  • 코틀린에서 중첩 클래스는 기본적으로 정적(static) 클래스처럼 다뤄짐
  • 객체 생성없이 접근 할 수 있음
class Outer {
    val ov = 5
    
    class Nested {
        fun greeting() { }
        /* 외부 프로퍼티 ov에는 접근 불가 */
    }
    
    fun outside() {
        val msg = Nested().greeting() // 객체 생성 없이 중첩 클래스의 메서드 접근
    }
}

fun main() {
    // static 처럼 객체 생성 없이 사용
    val output = Outer.Nested().greeting()
    
    val Outer.ov // !오류! 외부 클래스는 객체 생성없이 접근 불가
}

⬇️ 중첩 클래스에서 외부 프로퍼티에 접근하기

class Outer {
    class Nested {
        fun accessOuter() {
            println(country) // 컴패티언 객체에 접근
        }
    }

    companion object { const val country = "Korea" }
}

-> 컴패니언 객체는 객체 생성 없이 고정적인 메모리를 가지기 때문에 Nested클래스에서 Outer클래스의 프로퍼티인 country에 접근 가능

📌 이너 클래스 (Inner Class)

  • inner 키워드 사용
  • 중첩 클래스와 다르게 바깥 클래스의 멤버들에 접근 가능
  • 바깥 클래스의 private 멤버도 접근 가능
class Outer {
    private val ov = 5
    
    inner class Inner {
    	// 바깥 클래스의 private 프로퍼티인 ov에 접근
        fun getOV() = println(ov)
    }
}

📌 지역 클래스 (Local Class)

  • 특정 메서드의블록이나 init 블록과 같이 블록 범위에서만 유효한 클래스
class Outer {
    private val ov = 5

    fun localClassFunc() {
        class InnerClass(val iv: String) { // 지역 클래스 선언
            fun innerIV() = "$iv access $ov" // 외부 클래스 프로퍼티에 접근 가능
        }
        
        val innerClass = InnerClass("inner class")
        println(innerClass.iv)
    } // PowerOn 블록 끝
}

📌 익명 객체 (Anonymous Object)

  • 일회성으로 객체를 생성해 사용
interface anonymousObject {
    fun ao()
}

class Outer {
	// 익명 객체를 사용해 anonymousObject의 ao()를 구현
    val anonymousObject = object: anonymousObject {
        override fun ao() { /* 구현부 */ }
    }
    
    fun test() {
        anonymousObject.ao() // 익명 객체의 메서드 사용
    }
}

-> 메서드가 호출될 때마다 일회성 객체의 인스턴스가 만들어짐


2-3. 실드 클래스와 열거형 클래스

📌 실드 클래스 (Sealed class)

  • 객체를 만들 수 없음
  • 생성자는 기본적으로 private 이며, private이 아닌 생성자는 허용하지 않음
  • 같은 파일 안에서만 상속이 가능
  • 블록 안에 선언되는 클래스는 상속이 필요한 경우 open 키워드로 선언
sealed class Result {
    open class Success(val message: String): Result()
    class Error(val code: Int, val messeage: String): Result()
}

class Status: Result() // 실드 클래스 상속
class Inside: Result.Success("Status") // Result의 내부 클래스 Success 상속

📌 열거형 클래스 (Enum Class)

  • 여러 개의 상수를 선언하고 열거된 값을 조건에 따라 선택할 수 있는 특수한 클래스
  • 실드 클래스처럼 다양한 자료형을 다루지 못함
enum class 클래스 이름 [(생성자)] {
	상수1[()], 상수2[()], 상수3[()], ...
    [; 프로퍼티 혹은 메서드]
}

⬇️ 열거형 클래스 예시

enum class DayOfWeek(val num: Int) {
	MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4),
    FRIDAY(5), SATURDAY(6), SUNDAY(7); 
    // 세미콜론으로 끝을 알리고
    
    // 메서드를 포함할 수 있음
    fun day() { }
}

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

-> 필요한 경우 열거형 클래스 내부에 세미콜론(;)을 이용해 메서드를 포함할 수 있음

profile
장똑대와 안드로이드

0개의 댓글

관련 채용 정보