기록하는 습관

[Kotlin In Action]6장 - 코틀린 타입 시스템 본문

스터디/Kotlin In Action

[Kotlin In Action]6장 - 코틀린 타입 시스템

로그뉴 2023. 3. 13. 23:28

** 이 글은 Kotlin In Action을 읽고 정리한 글입니다. **

 

1. 널 가능성

fun strLenSafe(s: String?) = s.length()

타입 이름 뒤에 물음표(?)를 붙이면 그 타입의 변수나 프로퍼티에 null 참조를 저장할 수 있다.

 

val x:String? = null
val y:String = x

널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없다.

 

안전한 호출 연산자 ?.

null 검사와 메서드 호출을 한 번의 연산으로 수행한다.

val country = this.company?.address?.country

 

엘비스 ?:연산자

이항 연산자로 좌항을 계산한 값이 널인지 검사한다. 좌항 값이 널이 아니면 좌항 값을 결과로 하고, 좌항 값이 널이면 우항 값을 결과로 한다.

val info = xxxRepository.findById(id) ?: throw CustomException()

 

안전한 캐스트 as?

as?는 값을 대상 타입으로 변환할 수 없으면 null을 반환한다. 

val otherPerson = o as? Person ?: return false // 변환 실패시 null 반환

 

널 아님 단언 !!

어떤 값이든 널이 될 수 없는 타입으로 강제로 바꿀 수 있다.

여러 !! 단언문을 한 줄에 함께 쓰는 것을 피하라.

val sNotNull: String = s!!

 

let 함수

null이 될 수 있는 값을 null이 아닌 값만 인자로 받는 함수에 넘기는 경우.

let 함수는 자신의 수신 객체를 인자로 전달받은 람다에게 넘긴다. 

let을 중첩시키는 것보다, if를 사용해 모든 값을 한꺼번에 검사하는 편이 낫다.

email?.let { sendEmailTo(it) }

 

lateinit 나중에 초기화

프로퍼티를 나중에 초기화 할 수 있는 키워드로, 나중에 초기화하는 프로퍼티는 항상 var여야 한다.

private lateinit var myService: MyService

 

Null이 될 수 있는 타입 확장

널이 될 수 있는 타입의 확장 함수는 안전한 호출 없이도 호출 가능하다.

코틀린에서는 널이 될 수 있는 타입의 확장함수 안에서는 this가 널이 될 수 있다는 점이 자바와 다르다.

input.isNullorEmpty()
input.isNullorBlank()

 

타입 파라미터의 널 가능성

모든 타입 파라미터는 기본적으로 널이 될 수 있다.
널 가능 타입과 널 불가 타입 모두 타입 파라미터 인자로 사용 가능하다.

타입 파라미터 T를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 물음표가 없더라도 T가 널이 될 수 있는 타입이다.

fun <T> printHashCode(t: T) { // t는 Any? 타입으로 추론
    println(t?.hashCode()) 
}

fun <T: Any> printHashCode(t: T) { // t는 Any 타입으로 추론
     println(t.hashCode())
}

 

타입 파라미터가 널이 아님을 확실히 하려면 널이 될 수 없는 타입 상한을 지정해야 한다.

fun <T: Any> printHashCode(t: T) { // 널이 될 수 없는 타입 T
		println(t.hashCode())
}

 

자바의 널 가능성 애노테이션

자바 코드에 널 가능성 애노테이션을 사용하면 코틀린은 그 정보를 활용한다.

  • @Nullable String ‐> String?
  • @NotNull String ‐> String

JSR‐305 표준, 안드로이드, 젯브레인 애노테이션 등의 널 가능성 애노테이션 지원한다.

 

 

플랫폼 타입

널 관련 정보를 알 수 없는 자바 타입을 말한다.

널 가능 타입으로 처리해도 되고 널 불가 타입으로 처리해도 되기 때문에 널 가능성을 코틀린 코드에서 알맞게 처리해야 한다.

코틀린에서 플랫폼 타입을 선언할 수는 없고, 자바 코드에서 가져온 타입만 플랫폼 타입이 된다.

public class Person { 
    private final String name;
    public String getName() {
       return name;
    }   
}
fun yellAt(p: Person) {
    println(person.name.toUpperCase() + "!!!") // person.name이 null일 때 예외 발생
}

fun yellAtSafe(p: Person) {
    println((person.name ?: "Anyone").toUpperCase() + "!!!")
}

 

2. 코틀린의 원시 타입

코틀린은 원시 타입과 래퍼 타입을 구분하지 않는다.

 

숫자 변환

한 숫자 타입을 다른 타입 숫자로 자동 변환하지 않으므로 명시적으로 변환해야 함

숫자 리터럴은 컴파일러가 필요한 변환을 자동으로 처리 연산자를 각 타입에 대해 오버로딩하고 있다.

val b: Byte = 1
val l = b + 1L
val l1: Long = 42 // Int 타입 리터럴을 Long으로 컴파일러가 변환 
val i: Int = 42 
val l2: Long = i.toLong()

 

최상위 타입

val answer: Any = 42

Any 타입은 모든 널이 될 수 없는 타입의 조상 타입니다. 내부에서 Any는 Object에 대응한다. 코틀린에서 Any를 사용하면 자바 바이트코드의 Object로 컴파일된다.

  • Any 타입에는 널이 들어갈 수 없다! -> Any?를 사용해야 널 허용.
  • Any: toString, equals, hashCode 세 개의 메서드 제공

 

Unit 타입

Unit 타입은 자바의 void와 같은 기능을 한다.

  • 관심을 가질 만한 내용을 리턴하지 않는 함수의 리턴 타입으로 Unit 사용한다.
  • Unit은 void와 달리 타입, Unit이라는 단일 값을 가짐. 리턴 타입이 Unit인 함수는 묵시적으로 Unit을 리턴
interface Processor<T> {
    fun process(): T
}

class NoResultProcessor: Processor<Unit> {
    override fun process() {
        // 업무 처리 코드
    }
}

 

Nothing 타입

fun fail(message: String): Nothing {
     throw IllegalStateException(message)
}

Nothing 타입은 함수가 결코 정상적을 끝나지 않는다는 의미를 가지고 있다.

Nothing 타입은 아무 값도 포함하지 않는다. 함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 쓸 수 있다.

 

 

3. 컬렉션과 배열

읽기 전용과 변경 가능한 컬렉션

  • 인터페이스 분리
    • kotlin.collections.Collection : 조회 기능만 제공
    • kotlin.collections.MutableCollection : 수정 기능도 함께 제공

읽기 전용 컬렉션 인터페이스는 읽기 전용이고, 실제 구현체는 수정 가능할 수 있다.

 

 

코틀린 컬렉션과 자바

모든 코틀린 컬렉션은 그에 상응하는 자바 컬렉션 인터페이스의 인스턴스이다.

읽기 전용과 변경 가능 2가지를 제공한다.

 

 

 

 

Comments