2.1 코틀린 널 허용 타입 사용하기
- String?
val 변수의 널 허용성 검사
val p = Person("aa", null, "bb")
if (p.middle != null) {
// p.middle의 타입을 String? 대신 String으로 인식 -> 영리한 타입 변환 수행
val middleNameLength: Int = p.middle.length
}
- p가 한번 설정되면 그 값을 바꿀 수 없는 val 키워드로 선언됐기 때문에
var p1 = Person("aa", null, "bb")
if (p1.middle != null) {
// val middleNameLength = p.middle.length
val middleNameLength = p1.middle!!.length
}
- var이기 때문에 중간에 값이 변경 될 수도 있다고 가정 → 영리한 타입 변환 수행X
- 영리한 타입 변환이 수행되도록 우회하는 한가지 방법은 단언 연산자(!!)
- 단언 연산자(!!)는 널이 아닌 값으로 강제 → null이라면 nullPointerException 발생
- ⇒ 안전호출(?.)을 사용하는 것이 좋음
var p2 = Person("aa", null, "bb")
if (p2.middle != null) {
// null을 반환한다.
// val middleNameLength = p2.middle?.length
// null이면 0을 반환
val middleNameLength = p2.middle ?: 0
}
val p3 = Person("aa", null, "bb")
val p4 = p3 as? Person
- 안전 타입 변환 연산자(as?)
- 타입 변환이 올바르지 않을 경우 classCastException 방지
- p4은 Person? 타입이 된다.
2.2 자바에 널 허용성 지시자 추가
자바 코드와 상호 작용이 필요하고 널 허용성 애노테이션을 강제
- Xjsr305=strict 사용
build.gradle
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
build.gradle.kts
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
2.3 자바를 위한 메서드 중복 @JvmOverloads
코틀린 함수가 있을 때 자바에서 각 파라미터 값을 직접적으로 명시하지 않고 해당 코틀린 함수를 호출하고 싶을 때 @JvmOverloads 사용
fun main() {
// optional 또는 null 허용속성은 마지막에 위치시켜야함
fun addProduct(name: String, price: Double = 1.0, desc: String? = null) {
println("name = $name")
println("name = $price")
println("name = $desc")
}
addProduct("name")
addProduct("name", 5.0)
addProduct("name", 5.0, "desc")
}
@JvmOverloads 어노테이션을 추가한 함수를 디컴파일 하게 된다면?
@JvmOverloads
public static final void addProduct(@NotNull String name, double price, @NotNull String desc) {
Intrinsics.checkParameterIsNotNull(name, "name");
Intrinsics.checkParameterIsNotNull(price, "price");
Intrinsics.checkParameterIsNotNull(desc, "desc");
}
// @JvmOverloads
// public static final void addProduct(@NotNull String name, double price) {
// return addProduct$default(name, price, (String)null, 4, (Object) null);
// }
data class Product @JvmOverloads constructor(
val name: String,
val price: Double = 0.0,
val desc: String? = null
)
- @JvmOverloads 사용하기 위해서는 constructor 키워드 사용해야한다.
주의점
- 선택인자와 함께 생성자를 호출할 때는 상위 클래스의 유사한 생성자를 호출하는 것이 아니라 모든 생성자 호출이 가장 많은 인자를 요구하는 단 하나의 생성자를 통해 이루어진다는 점
- @JvmOverloads 가 붙어 있는 생성자 호출은 같은 개수 인자를 갖는 super를 호출하지 않음
- 기본 인자와 함께 모든 인자를 요구하는 생성자를 호출
2.4 명시적으로 타입 변환하기
자동 승격
int myInt = 3;
long myLong = myInt;
Integer myInteger = 3;
// Long a = myInteger; - 컴파일 X
Long myWrappedLong = myInteger.longValue(); - long으로 추출후 래퍼타입으로 감쌈
Long b = Long.valueOf(myInteger); - 래퍼타입을 벗겨 int타입을 얻고 long 타입으로 승격후 다시 래퍼
- 래퍼 타입을 직접 다루는 것은 언박싱을 개발자가 해야한다.
- 코틀린에서는 기본타입을 직접적으로 제공하지 않음
- 즉 int, long 이런것이 없고 Int, Long 등 래퍼타입만 있다.
- toInt 같은 형태 변환 메서드 제공해줌
2.5 다른 기수로 출력하기
올바른 기수를 위한 확장 함수 toString(radix: Int)
val a = 42;
// 진법을 파라미터로
a.toString(2)
- radix의 최솟값은 2
- radix의 최대값은 36
2.6 숫자 거듭제곱하기
내장 거듭제곱 연산자가 없음
- Float과 Double에는 확장함수 pow가 있지만
- Int나 Long에는 pow함수가 없음
2.toDouble().pow(8).toInt()
- 시그니처의 확장 함수 정의
fun Int.pow(x: Int) = toDouble().pow(x).toInt()
fun Long.pow(x: Int) = toDouble().pow(x).toLong()
- infix로 연산자 정의
infix fun Int.`**`(x: Int) = toDouble().pow(x).toInt()
...
fun Int.pow(x: Int) = `**`(x)
2.7 비트 시프트 연산자 사용
비트 시프트를 위한 shr, shl, ushr 이 있다.
shl: 부호 있는 왼쪽 시프트
shr: 부호 있는 오른쪽 시프트
ushr: 부호 없는 오른쪽 시프트
// 101 -> 10
5 shr 1
/// 큰 정수 2개의 중간값 찾기
val high = (0.99 * Int.MAX_VALUE).toInt()
val low = (0.75 * Int.MAX_VALUE).toInt()
val mid1 = (high + low) / 2
val mid2 = (high + low) ushr 1
// .. low <= mid1 <= high ..이 <= <=(between)이란 뜻
mid1 !in low..high
mid2 in low..high
2.8 비트 불리언 연산자 사용하기
비트 값에 mask를 적용
- and
- or
- xor
- inv (모든 비트를 뒤집음)
println(4 === 0b0000_0100)
println(251 === 0b1111_1011)
println(4.inv()) // -5
4를 다 뒤집으면 1111 1011이 되는데 컴퓨터는 2의 보수를 쓰기 때문에
1111 1011 → - (0000 0100 + 1) 이 돼서 -5가 된다.
2.9 to로 pair 인스턴스 생성하기 (유용할 듯?)
pair 클래스의 인스턴스를 생성 할 때 to 함수를 사용한다.
- pair 인스턴스의 리스트로부터 맵을 생성하는 mapOf 와 같은 맵 생성을 위한 함수를 제공함
- pair는 first와 second라는 이름의 두개의 원소를 갖는 데이터 클래스
- first, second의 속성은 제네릭 값에 해당한다.
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
val p1 = Pair("a", 1)
val p2 = "b" to 2
println(p1.first)
println(p1.second)
println(p2.first)
println(p2.second)
val (x, y) = "b" to 3
println(x)
println(y)
- 트리플이라는 3개의 값을 가지고 있는 클래스는 인스턴스를 생성하는 확장 함수가 존재하지 않는다.
'책 > 코틀린 쿡북' 카테고리의 다른 글
4장 - 함수형 프로그래밍 (0) | 2022.09.27 |
---|---|
3장 - 코틀린 객체 지향 프로그래밍 (0) | 2022.07.18 |
1장 - 코틀린 설치와 실행 (0) | 2022.07.03 |