모바일&임베디드/안드로이드

[Kotlin] 고급 문법2(Data class, Object, Companion Object)

차가운오미자 2021. 6. 15. 11:14

 

3. Data Class

데이터만을 다루는 클래스에 대한 것

 

* 선언: ()안에는 parameter을 저장해주면 된다.

data class Ticket(val companyName : String, 
                 val name : String, 
                 var date : String, 
                 val seatNum : Int)

* 제한사항

1) 기본 생성자에 최소 하나의 파라미터 필요

2) 기본 생성자의 파라미트는 val or var

3) data class는 abstract, open, sealed, inner가 될 수 없다

 

* 특징

- 하나의 프로퍼티처럼 사용 가능하다

- toString, hashcode생성 등 자동으로 가능해짐 -> canomical methods라고 부름

 

* CANOMICAL METHOD

- Any 에 선언된 메소드 (Any = Java의 Object class와 비슷)

- 코틀린의 모든 인스턴스가 갖고 있는 메소드를 뜻한다

1) equals(other: Any?): Boolean // 데이터 클래스 간에 값이 일치하는지 확인

2) hashCode() : Int // 인스턴스의 숫자표현, 해시코드가 같은 인스턴스에서 여러 번 호출될 때 항상 같은 값 반환

3) toString() : String // 인스턴스의 문자열 표현

 

+) copy(): 원하는 매개변수를 오버라이딩해서 새로운 데이터클래스 인스턴스를 생성할 수 있게 해줌. 깊은 복사(메모리까지 복사됨)

class TicketNormal(val compname : String, 
                  val name : String,
                  var data: String,
                  val seatNum : Int)
fun main(){

    val ticketA = Ticket("Korean Air", "Tara", "2020.01.01.", 53)
    val ticketB = TicketNormal("Korean Air", "Jennie", "2020.01.01", 54)

    println(ticketA) // 내용을 프린트
    println(ticketB) // 주소값을 프린트

}

 

data class를 사용했을 때는, 내용이 데이터 값으로 저장되어 프린트 했을 때 데이터 값들이 뜨지만

class를 사용하면 주소값이 프린트된다

 

4. Object

- 프로젝트 내에서 딱 한 번만 생성되는 객체이다.

- 불필요한 메모리 사용을 방지할 수 있다.

- 처음 컴파일 할 때 만들어지는 인스턴스가 계속 사용된다. = singleton pattern 사용

- 클래스나 인터페이스를 상속할 수 있다.

object CarFactory{
    val cars = mutableListOf<Car>()
    // 리스트가 상수로 선언되었지만 mutableList여서 add, remove 등으로 변경 가능
    fun makeCar(horsePower: Int) : Car{
        val car = Car(horsePower)
        cars.add(car)
        return car
    }
}

data class Car(val horsePower: Int)

fun main(){
    val car = CarFactory.makeCar(horsePower = 10)
    val car2 = CarFactory.makeCar(horsePower = 200)
    // 하나의 오브젝트로 여러 객체 생성함

    println(car)
    println(car2)
    println(CarFactory.cars.size.toString())
}

 

 

5. Companion Object

- 컴패니언이라는 이름에서 알 수 있듯이 클래스가 메모리에 적재될 때 동반되는 객체(object)이다

- 클래스 내에 일부분을 싱글톤 객체로 가지고 싶을 때 사용한다.

- 클래스에 딱 한 번만 있을 수 있으며 이 오브젝트와 그 멤버는 클래스의 인스턴스를 통해서가 아니라 이를 포함하는 클래스 이름을 통해서만 액세스 할 수 있다.

- Java의 static과 비슷하지만 차이점도 있다.

- 컴패니언이라고 할 필요없이 바로 접근도 가능하다. -> 인스턴스로 접근 불가능하다

class Book private constructor(val id : Int, val title: String){
    // 컴패니언 오브젝트를 사용하는 예시를 위해 프라이빗한 생성자를 사용한다. 
    
    companion object{
        fun create() = Book(0, "Animal Farm")
        // private property or method를 읽어올 수 있도록 함
    }
}
class Book2 private constructor(val id : Int, val title: String){
    // 프라이빗 생성자를 사용하고 있음
    // 그럴 때 컴패니언 오브젝트를 통해 바깥에서 프라이빗한 이 생성자를 호출할 수 있도록 하는 거임

    companion object BookFactory :IDProvider{
        // 이름을 줄 수 있음
        // IDProvider라는 인터페이스를 상속함

        override fun getId(): Int {
            // getId 함수가 오버라이드 되어야 함
            return 133
        }

        val myBook =  "new Book"
        fun create() = Book2(getId(), myBook)
        // private property or method를 읽어올 수 있도록 함
    }
}

interface IDProvider{
    fun getId() : Int
}
fun main(){

    val book = Book.Companion.create()

// 컴패니언이라고 할 필요없이 바로 접근도 가능하다.
    val book2 = Book2.create() // companion 생략 가능

    println("${book.id}, ${book.title}")
    println("${book2.id}, ${book2.title}")

    val bookId = Book2.BookFactory.getId()
    // 위에서는 그냥 Book.Companion.create()를 통해 컴패니언 오브젝트의 함수를 불러왔지만,
    // 이 경우에는 BookFactory라는 이름을 주었기 때문에 이걸 매개로 사용해야 함
    println("${bookId}")

    println(book3.BookFactory.getId()) //ERROR, cannot access by instance
    println(Book2.getId())
    println(Book2.BookFactory.getId())
}

 

 

* java static과 다르게

class Car(){
    companion objecter{
        val cmpname = "Hyundai"
        fun geton() = "Got on a car!"
    }
}

fun main(){
    val comp1 = Car.Companion
    println(comp1.cmpname)
    println(comp1.geton())

    val comp2 = Car.Companion
    println(comp2.cmpname)
    println(comp2.geton())
}

변수에 컴패니언 오브젝트를 할당해서 사용할 수 있다.