object

코틀린에서 object 키워드는 싱글톤 객체를 만들 때 사용된다.

ojbect 클래스 안에는 val, var, method 모두 가질 수 있고, 상속을 받는 것도 가능하다.

object Util : Runnable {
    val width = 300
    var time = 0
    fun doSomething() { }
    override fun run() { }
}

사용할 때는 Util.width, Util.doSomething() 이런 식으로 사용할 수 있다.

object 는 객체를 생성할 수 없다. 코틀린 컴파일러는 object를 클래스로 취급하지 않고 이미 객체인 상태로 취급한다.

내부적으로는 Util클래스의 static 인스턴스라고 표현한다.

companion object

companion object 는 클래스 안에 정의한 싱글톤이다.

class Example {
    companion object {
    	val width = 300 
    }
}

클래스의 companion object 멤버에 접근하려면, 클래스 이름으로 참조하면 된다.

Example.width 이런 식으로 접근할 수 있다.

만약 companion object 객체 자체에 대한 참조가 필요하다면 Example.Companion 이런 식으로 할 수 있다.

또는 companion object에 이름을 붙여서 접근할 수도 있다.

class Example {
    companion object Inner {
    }
}

Example.Inner로 companion object를 참조할 수 있다.

companion object의 멤버가 클래스의 static 멤버가 되는 것은 아니다.

companion object의 멤버에 접근하면 코틀린 컴파일러는 싱글톤 객체로 라우팅한다.

@JvmStatic 어노테이션을 사용해서 static 멤버로 만들 수 있다.

 

차이점

  • 초기화 시점
    • object는 사용할 때 초기화된다.
    • companion object는 클래스가 로드될 때 초기화된다.

'Programming > 언어' 카테고리의 다른 글

[Kotlin] Coroutine 기본 개념, 동작 원리  (0) 2023.03.19
[Kotlin] Delegation  (0) 2023.03.01
[Kotlin] Generics 공변성, 반공변성(out, in)  (0) 2023.02.26
[Kotlin] Data Class  (0) 2023.02.15
[Kotlin] Collection의 View  (0) 2023.02.13

Kotlin에서 List, Map, Set같은 Collections 클래스를 사용할 때, List<String> 이런식으로 제네릭 타입을 지정할 수 있다.

제네릭을 사용하면 타입 안정성을 얻을 수 있다.
하지만 제네릭으로 지정한 타입 이외에도 좀 더 유연하게 사용하고 싶을 수 있다.
그래서 코틀린에서는 공변성과 반공변성을 허용한다.

  • 공변성이란, 파라미터 타입 T의 자식클래스를 T의 자리에 사용할 수 있도록 허용하는 것이다.
  • 반공변성이란, 파라미터 타입 T의 부모클래스를 T의 자리에 사용할 수 있도록 허용하는 것이다.
open class Fruit
class Banana : Fruit()
class Orange : Fruit()

fun receiveFruits(fruits: Array<Fruit>) {
    println("${fruits.size}")
}

receiveFruits함수 안에서는 fruits의 크기만 출력하고 있다.

하지만, 파라미터로 Array<Banana>, Array<Orange>를 전달할 수 없다.

왜냐하면 함수 안에서 fruits.add(Orange()) 을 한다고 할 때, Array<Banana>를 전달했다면 타입 에러가 나기 때문이다.

하지만 fun receiveFruits(fruits: List<Fruit>)라면 어떨까? (Array -> List)

된다. 왜?

  • Array는 Mutable 하지만, List는 Immutable하기 때문이다.
    • 따라서 함수 내부에서 fruits.add(Orange()) 이런 연산 자체를 하지 못한다.
  • 각 타입이 정의되는 곳을 보면 Array는 Array<T>, List는 List<out T>로 정의한다.

out 에 대해 알아보자.

out은 공변성을 사용할 수 있게 해주는 키워드이다. 즉, 파라미터 타입으로 T의 자식타입을 사용할 수 있게 해준다.
어떻게 그렇게 하는 걸까?

fun copyFromTo(from: Array<out T>, to: Array<T>) 처럼 선언 했을 때, 함수 안에서 from에 "쓰기" 작업을 하지 않는다는 보장이 있어야 한다. "쓰기"작업이 있는지 없는지는 컴파일러가 판단해 있다면 컴파일 시간에 오류를 준다.

이렇게 Array라는 제네릭 클래스를 정의한 곳이 아닌 사용하는 쪽에서 out으로 공변성을 이용하는 것을 사용처 가변성 또는 타입 프로젝션이라고 부른다.

List와 같이 제네릭 클래스를 정의할 때 out으로 공변서을 이용한다면 선언처 가변성이라고 한다.

예시

fun copyFromTo(from: Array<Fruit>, to: Array<Fruit>) {
    for (i in 0 until from.size) {
        to[i] = from[i]
    }
}

val fruits = Array<Fruit>(3) { _ -> Fruit() }
val bananas = Array<Banana>(3) { _ -> Banana() }
copyFromTo(bananas, fruits)

이 경우 from은 "읽기"만 하고있어 로직 상으로는 전혀 문제가 없지만, Array<Banana>Array<Orange>타입이 들어갈 수 없다.
Fruit의 자식 클래스도 전달할 수 있게 만드려면 from: Array<out Fruit>으로 선언하면 된다.

in; 반공변성은 무엇인가.

공변성과 반대로, 파라미터 타입에 부모클래스를 사용하고 싶을 수 있다.

위의 예시에서 to위치에 Fruit의 부모클래스를 받고 싶다면 in 키워드를 사용하면 된다.

to: Array<in Fruit> 로 선언하면 Fruit의 부모타입도 받을 수 있다.

'Programming > 언어' 카테고리의 다른 글

[Kotlin] Delegation  (0) 2023.03.01
[Kotlin] object, companion object  (0) 2023.02.26
[Kotlin] Data Class  (0) 2023.02.15
[Kotlin] Collection의 View  (0) 2023.02.13
[Kotlin] Unit, Any, Nothing 클래스  (0) 2023.02.12

코틀린의 데이터 클래스는 특정한 행동, 동작보다는 데이터를 옮기는 데 특화된 클래스이다.

data class Student(val name: String, val age: Int, var grade: String)

이런 식으로 작성할 수 있다.

  • 생성자에는 attribute가 1개 이상 필요하다.
  • val, var가 아닌 파라미터는 사용할 수 없다.
  • equals(), hashCode(), toString(), copy() 메소드들을 기본적으로 제공해준다.
  • 구조분해가 가능하다. 

{} 바디 안에 attribute와 메소드를 추가할 수도 있다.


주의

  • copy()는 새로운 객체를 만들어 복사하지만, 객체 내부의 참조들은 shallow copy(얕은 복사)가 된다.
  • 예를 들어 다음과 같다.
data class School(val name: String,val students: List<Student>)

val school1 = School("Korea", arrayOf(Student(...), ...))
val school2 = school1.copy()
  • 위와 같은 코드에서 school1과 school2는 각각 다른 인스턴스를 참조하지만, students에 대한 참조는 같다.
    • students가 만약 MutableList이라면 주의가 필요하다.

'Programming > 언어' 카테고리의 다른 글

[Kotlin] object, companion object  (0) 2023.02.26
[Kotlin] Generics 공변성, 반공변성(out, in)  (0) 2023.02.26
[Kotlin] Collection의 View  (0) 2023.02.13
[Kotlin] Unit, Any, Nothing 클래스  (0) 2023.02.12
[Kotlin] 타입추론  (0) 2023.02.12

코틀린의 각 컬렉션 클래스들은 Mutable, Immutable 2가지가 존재한다.

최근 버전의 자바는 Immutable 컬렉션을 제공하지만, Mutable 컬렉션과 같은 인터페이스를 구현하고 있기 때문에, add와 같은 메소드들을 호출할 수 있고, 이는 실행시간에 예외를 발생시킨다.

코틀린은 Immutable 컬렉션의 연산이 불가능하다는 것을 실행시간에 알리지 않는다.

그래서 View가 존재한다.

  • List, Set, Map은 각각 뷰를 2개씩 가지고 있다. 
    • Mutable View - 읽기, 쓰기
    • Immutable View - 읽기 전용
  • mutable이든, immutable이든 자바의 기본 컬렉션에 매핑된다.
    • 쓰기, 읽기 제한을 하는 것은 코틀린 컴파일러의 역할이다.
    • 따라서 실행 시간에 오버헤드가 없다.
  • 예를들어, MutableList, List 컬렉션이 있다.
    • 둘 다 코틀린의 ArrayList에 매핑되지만, List를 사용할 때는 add같은 쓰기 연산을 할 수 없다. 

 

  • Immutable View가 스레드 안정성을 보장하지는 않는다.
    • immutable view가 참조하고 있는 컬렉션은 자바의 mutable 컬렉션이다.
      • 실제로 매핑된 컬렉션을 참조하는 다른 뷰가 변경할 수도 있다. 

 

'Programming > 언어' 카테고리의 다른 글

[Kotlin] Generics 공변성, 반공변성(out, in)  (0) 2023.02.26
[Kotlin] Data Class  (0) 2023.02.15
[Kotlin] Unit, Any, Nothing 클래스  (0) 2023.02.12
[Kotlin] 타입추론  (0) 2023.02.12
[Kotlin] 표현식과 명령문  (0) 2023.02.12

+ Recent posts