Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- table not found
- tolerated
- Spring Batch
- 티스토리챌린지
- kotlin
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- mp4fpsmod
- python
- 자원부족
- 개성국밥
- 오블완
- PytestPluginManager
- 깡돼후
- JanusWebRTCServer
- taint
- PersistenceContext
- Value too long for column
- JanusGateway
- VARCHAR (1)
- vfr video
- 코루틴 컨텍스트
- 겨울 부산
- terminal
- preemption #
- k8s #kubernetes #쿠버네티스
- pytest
- JanusWebRTCGateway
- 코루틴 빌더
- JanusWebRTC
- 달인막창
Archives
너와 나의 스토리
[Kotlin] 코틀린 타입 시스템 - ? / !! /.? / as? / let 함수 / lateinit 본문
Programming Language/Kotlin
[Kotlin] 코틀린 타입 시스템 - ? / !! /.? / as? / let 함수 / lateinit
노는게제일좋아! 2021. 6. 17. 09:48반응형
타입 시스템(Type system)
NULL 가능성(Nullability)
- NullPointerException(NPE) 오류를 피할 수 있도록 돕는 코틀린 타입 시스템의 특성이다.
- 코틀린에서는 널이 될 수 있는지 여부를 타입 시스템에 추가함으로써 컴파일러가 여러 가지 오류를 컴파일 시 미리 감지해서 실행 시점에 발생할 수 있는 예외의 가능성을 줄일 수 있다.
NULL이 될 수 있는 타입
- 코틀린의 모든 타입은 기본적으로 널이 될 수 없는 타입이다.
- 코틀린은 널이 될 수 있는 타입을 명시적으로 지원한다.
- 타입 옆에 물음표(?)를 표시한다.
- NULL이 될 수 있는 타입의 변수이지만, 현재 NULL이 아님을 주장할 수 있다.
- 느낌표 2개(!!)를 변수 뒤에 붙인다.
- 이 표시를 통해 null이 될 수 없는 변수에 null이 될 수 있는 타입을 주입할 수 있다.
- 널이 될 수 있는 타입인 변수에 대해 메서드를 호출하면 NPE가 발생할 수 있으므로, 코틀린은 그런 메서드 호출을 금지함으로써 NTE 발생을 예방한다.
// var a:Int = null -> error
var a:Int? = null
var b:Int? = 10
// var c:Int = b -> error
var c:Int = b!!
안전한 호출 연산자: ?.
- (?.)은 null 검사와 메서드 호출을 한 번의 연산으로 수행한다.
- 예:
-
foo?.bar()
- foo가 null이면 bar() 메서드 호출이 무시되고 null이 결과 값이 된다.
- foo가 null이 아니면 bar() 메서드를 정상 실행하고 결과값을 얻어온다.
-
class Employee(val name: String, val manager: Employee?)
fun managerName(employee: Employee): String? = employee.manager?.name
엘비스 연산자: ?:
- null 대신 사용할 디폴트 값을 지정할 때 편리한 연산자
- 사용 방법:
-
fun foo(s: String?) { val t: String = s ?: "" }
- s가 null이면 ""(빈 문자열)을 t에 넣고
- s가 null이 아니면 t에 s를 넣는다.
-
- 예 1:
-
fun strLenSafe(s: String?): Int = s?.length ?: 0
-
- 예 2:
-
var b:Int? = 10 // var d:Int = b ->> error var d:Int = b ?: 3
- b가 null이면 3을 준다고 지정해 b가 null이 아님을 증명함으로써 디폴트 타입을 가지는 d에게 null이 될 수 있는 타입을 가진 b를 넘겨줄 수 있다.
-
- 코틀린에서는 return이나 throw 등의 연산도 식이다.
- 따라서 엘비스 연산자의 우항에 return, throw 등의 연산을 넣을 수 있다.
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person(val name: String, val company: Company?)
fun printShippingLabel(person: Person) {
val address = person.company?.address ?: throw IllegalArgumentException("No address")
with(address) {
println(streetAddress)
println("$zipCode $city, $country")
}
}
fun main(args: Array<String>) {
val address = Address("Elsestr. 47", 88432, "Munich", "Germany")
val jetbrains = Company("JetBrains", address)
val person = Person("Dmitry", jetbrains)
printShippingLabel(person)
/*
* output:
* Elsestr. 47
* 88432 Munich, Germany
* */
}
- 다음과 같이 with 연산자를 사용하면 불필요한 중복을 줄일 수 있다.
- with를 사용하지 않았더라면 println(address.streetAddress)처럼 사용해야 하지만, with를 사용함으로써 address를 생략하고 필드 값을 호출할 수 있다.
fun main(args: Array<String>) {
val jetbrains = Company("JetBrains", null)
val person = Person("Dmitry", jetbrains)
printShippingLabel(person)
/*
output:
Exception in thread "main" java.lang.IllegalArgumentException: No address
at MainKt.printShippingLabel(main.kt:6)
at MainKt.main(main.kt:18)
*/
}
안전한 캐스트: as?
- 자바 타입 캐스트와 마찬가지로 대상 값을 as로 지정한 타입으로 바꿀 수 없다면 ClassCastException이 발생한다.
- 그래서 자바에서는 보통 is를 통해 미리 as로 변환 가능한 타입인지 검사해 봄
- as? 연산자는 어떤 값을 지정한 타입으로 캐스트하고, 변환할 수 없으면 null을 반환한다.
- 예:
foo as? Type
- foo is Type이면 foo는 Type으로 변환하고
- foo !is Type이면 null을 반환한다.
- 엘비스 연산자와 함께 사용
-
class Person(val name: String, val company: Company?) { override fun equals(o: Any?): Boolean { val otherPerson = o as? Person ?: return false return otherPerson.name == name && otherPerson.company == company } }
- as?을 토해 o가 Person을 캐스트 안되면 null이 되고, 이는 엘비스 연산자(?:)로 인해 false가 리턴된다.
-
let 함수
- let 함수를 안전한 호출 연산자와 함께 사용하면 원하는 식을 평가해서 결과가 널인지 검사한 다음에 그 결과를 변수에 넣는 작업을 간단한 식을 사용해 한꺼번에 처리할 수 있다.
fun sendEmailTo(email: String) {
println("Sending email to $email")
}
fun main(args: Array<String>) {
var email: String? = "yole@example.com"
// sendEmailTo(email) -> error
email?.let { sendEmailTo(it) }
}
나중에 초기화할 프로퍼티
- 코틀린에서 클래스 안의 널이 될 수 없는 프로퍼티를 생성자 안에서 초기화하지 않고 특별한 메서드 안에서 초기화할 수 없다.
- 코틀린에서는 일반적으로 생성자에서 모든 프로퍼티를 초기화해야 한다.
- 게다가 프로퍼티 타입이 널이 될 수 없는 타입이라면 반드시 널이 아닌 값으로 그 포로퍼티를 초기화해야 한다.
- 그런 초기화 값을 제공할 수 없으면 널이 될 수 있는 타입을 사용할 수 밖에 없는데, 이러면 모든 접근에 대해 널 검사를 넣거나 !! 연산자를 써야한다.
- 예:
class MyTest { private var myService: MyService? = null @Before fun setUp() { myService = MyService() } @Test fun testAction() { Assert.assertEquals("foo", myService!!.performAction()) } }
- lateinit 변경자: 프로퍼티를 나중에 초기화
- 나중에 초기화하는 프로퍼티는 항상 var여야 한다.
- val 프로퍼티는 final 필드로 컴파일되며, 생성자 안에서 반드시 초기화해야 한다.
- 예:
class MyTest { private lateinit var myService: MyService @Before fun setUp() { myService = MyService() } @Test fun testAction() { Assert.assertEquals("foo", myService.performAction()) } }
널이 될 수 있는 타입 확장: isNullOrEmpty(), isNullOrBlank
- 널이 될 수 있는 타입에 대한 확장 함수를 정의하면 null 값을 다루는 강력한 도구로 활용할 수 있다.
var emptyString = ""
println(emptyString.isNullOrBlank()) // output: True
println(emptyString.isNullOrEmpty()) // output: True
타입 파라미터의 널 가능성
- 타입 파라미터 T를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 물음표가 없더라도 T가 널이 될 수 있는 타입이다.
- t가 널이 될 수 있으므로 안전한 호출(?.)을 써야만 한다.
fun <T> printHashCode(t: T) {
println(t?.hashCode())
}
fun main(args: Array<String>) {
printHashCode(null) // output: null
}
- 타입 파라미터가 널이 아님을 지정할 수 있다. -> 타입 상한(upper bound)를 지정
fun <T : Any> printHashCode(t: T) {
println(t.hashCode())
}
fun main(args: Array<String>) {
// printHashCode(null) -> error
}
출처:
- [Kotlin IN ACTION]
반응형
'Programming Language > Kotlin' 카테고리의 다른 글
[Kotlin] 컬렉션과 배열 (0) | 2021.06.18 |
---|---|
[Kotlin] 코틀린의 원시 타입 (1) | 2021.06.18 |
[Kotlin] 람다 식(lambda expression)과 멤버 참조 (0) | 2021.06.16 |
[Kotlin] 클래스와 인터페이스2 - 데이터 클래스/클래스 위임/object 키워드 사용 (0) | 2021.06.15 |
[Kotlin] 클래스와 인터페이스1 - 초기화(주 생성자와 부 생성자)/가시성 변경자/프로퍼티 (0) | 2021.06.14 |
Comments