📕Null
- 참조 값 중에서 아무것도 참조하지 않는 경우를 나타내는 특별한 참조값들
- 여기서 참조는 어떤 할당된 객체도 가리키지 않는 참조를 뜻한다.
- 다른 참조와 비슷하게 동작하지 않는다.
🔎자바 vs 코틀린
💻자바
모든 참조 타입의 변수에 널을 대입할 수 있지만, 이때 이 참조 타입(하지만 값은 널)에 정의된 메서드나 프로터 리를 사용하려고 하면 NullPointerException(NPE)가 발생. --> 런타임에 프로그램을 실행해봐야 오류를 찾을 수 있는 최악의 오류
💻코틀린
널 값이 될 수 있는 참조 타입과 널 값이 될 수 없는 참조타입을 구분 해고 널 발생 여부를 컴파일 시점으로 옮겨주기 때문에 NullPointerException(NPE) 예외를 상당 부분 막을 수 있다.
📗널이 될 수 있는 타입
코틀린 타입 시스템의 중요한 특징은 널 값을 포함하는 타입과 그렇지 않은 타입을 구분하는 능력이다.
자바-> 모든 참조 타입이 날이 될 수 있기에, 컴파일러는 참조 타입의 변수가 null 값이 아닌 값만 포함한다는 사실을 보장 x
코틀린-> 날이 될 수 있는 참조 타입이 정해져 있다. String 같은 타입에 null값을 대입할 수는 없다.
fun main(){
println(isLetterString("abc"))
println(isLetterString(null)) //null값은 String에 적절한 value가 아니다.
}
fun isLetterString(s: String) : Boolean {
if(s.isEmpty()) return false
for(ch in s){
if(!ch.isLetter()) return false
}
return true
}
만약 널이 될 수도 있는 값을 받는 함수를 작성하려면 파라미터 타입 뒤에 물음표(?)를 붙여서 타입을 널이 될 수 있는 타입으로 지정해야 한다.
fun isBooleanString(s:String?)=s=="false"||=="true"
String? 같은 타입은 널이 될 수 있는 타입이라 불린다.
모든 날이 될 수있는 타입은 원래 타입에 속하는 모든 값으로 이뤄진 집합을 null로 확장한 집합이 값의 집합이 된다.
- 널이 될 수 있는 타입의 변수에 항상 널이 될 수 없는 타입의 값을 대입할 수 있다.
- 널이 될 수 없는 타입의 변수에 널이 될 수 있는 타입의 값을 대입할 수는 없다.
fun main(){
println(isBooleanString(null)) //Ok
val s : String? = "abc" //Ok
val ss: String = s //Error
}
Int나 Boolean 같은 원시 타입도 널이 될 수 있는 타입이 존재한다. 하지만 원시 타입의 널이 될 수 있는 타입은 항상 박싱 한 값만 표현한다.
여기서 박싱이란 값 형식을 참조 형식으로 변환해주는 것을 의미한다.
- 값 형식 : 해당 데이터를 직접적으로 스택(stack) 메모리에 저장함
- 참조 형식은 힙(heap) 메모리에 데이터를 저장함.
⚡Nothing?
- 가장 작은 날이 될 수 있는 타입
- 널 상수 이외의 어떤 값도 포함하지 않음.
- null값 자체이며 다른 모든 널이 될 수 있는 타입의 하위 타입이다.
⚡Any?
- 가장 큰 날이 될 수 있는 타입
- 코틀린 타입 시스템 전체에서 가장 큰 타입
- 널이 될 수 있는 모든 타입과 널이 될 수 없는 모든 타입의 상위 타입이다.
💻날이 될 수있는 타입은 원래 타입에 들어있는 어떤 프로퍼티나 메서도드 제공하지 않는다.
예를 들어 s: String?이라면 s.isEmpty()등 메서드나 프로퍼티를 사용하지 못함.
📗널 가능성과 스마트 캐스트
널이 될 수있는 값을 처리하는 가장 직접적인 방법은 해당 값을 조건문을 사용해 null과 비교하는 것이다.
fun isLetterString(s: String?) : Boolean {
if(s==null ) return false
//만약 s가 null이면 for 문을 가기전에 return 되므로 for문에 들어갈 수 있는 s는 절대로 null값이 될 수 없다.
for(ch in s){
if(!ch.isLetter()) return false
}
return true
}
원래라면 널이 될 수 있는 타입은 for문과 String에 대한 메서드나 프로퍼티를 사용하지 못하지만, null에 대한 검사를 추가하면 컴파일된다.
왜 이런 일이 발생할까?
그것은 코틀린에서 스마트 캐스트라고 불리는 기능이 컴파일을 가능하게 해 준다.
위 코드를 예를 들어서 쉽게 설명하자면 if(s==null) 다음 문장은 return으로 끝나기 때문에 만약 s가 null일 경우 return 문으로 끝나기 때문에 만약 다음 if문 다음 문장이 실행될 경우 변수 s[널이 될수 있는 값]를 널이 될 수 없는 String 타입으로 변환하고, 나머지 함수 본문을 실행한다.
스마트 캐스트를 실행하려면 대상 변수의 값이 검가 지점과 사용 지점 사이에서 변하지 않는다고 컴파일러가 확신할 수 있어야 한다. 만약 널 검사와 사용 지점 사이에서 값이 변경되는 경우에는 스마트 캐스트가 작동하지 않는다.
var s =readLine()
if(s!=null){
s=readLine()
println(s.length())
}
📗널 아님 단언 연산자
readLine() 함수와 관련해!! 연산자를 이미 살펴봤다.!! 연산자는 널 아님 단언이라고 부르는데, KotlinNullPointerException 예외를 발생시킬 수 있는 연산자다.
이 연산자가 붙은 식의 타입은 원래 타입의 날이 될 수 없는 버전이다.
널 값을 역참조 하려 할 때 예외를 던지는 동작을 부활시킴.
📗안전한 호출 연산자
날이 될 수 있는 타입의 값에 대해서는 그에 상응하는 널이 될 수 없는 타입의 값에 있는 메서드를 사용할 수 없다고 이미 설명했다. 하지만 특별한 안전한 호출 연산을 사용하면 이런 제약을 피할 수 있다.
fun readInt() = readLine()?.toInt()
fun readInt(): Int?{
val tmp= readLine()
return if(tmp!=null) temp.toInt() else null
}
두 함수는 같은 함수이다.
안전한 호출 연산자는 수신 객체가 날이 아닌 겨웅 일반적인 함수 호출 처럼 작동한다.
--> 수신 객체가 날이 아닌 경우에는 의미있는 일을 하고, 수신 객체가 널인 경우에는 벌을 반환하라!
📗널 복합 연산자
- 널이 될 수 있는 값을 다룰 때 유용한 연산자
- 형태는?:
- 엘비스 연산자를 사용하면 널을 대신할 디폴트 값을 지정할 수 있다.
- 엘비스 연산자라고도 부른다.
fun main(){
sayHello("John")
sayHello(null)
}
fun sayHello(name : String?) {
println("Hello, " + (name?: "Unknown"))
}
이 연산자의 결과는 왼쪽 피연산자가 날이 아닐 경우에는 왼쪽 피연산자의 값이고, 왼쪽 피연산자가 벌일 경우에는 오른쪽 피연산자의 값이다.
fun sayHello(name : String?){
println("Hello ",+(if(name!=null) name else "Unknown"))
두 sayHello는 같은 함수이다.
안전한 연산과 엘비스 연산자를 조합해서 수신 객체가 널 일 때의 디폴트 값을 지정할 수 있다.
val n = readLine()?.toInt() ?: 0
위 코드는 프로그램의 표준 입력이 널을 반환할 경우 0을 n에 반환한다.
엘비스 연산자 오른쪽에는 return이나 throw 같은 제어 흐름을 깨는 코드도 넣을 수 있다.
val currentName = name?: return "Unknown"
✔정리
- 코틀린은 자바와 달리 널 값이 될 수 있는 참조 타입을 확실히 구분해주기에 NPE 예외를 상당 부분 막을 수 있다.
- 날이 될 수도 있는 타입을 만들려면 파라미터 타입 뒤에 물음표를 붙여서 지정해준다.
- 널이 될 수 있는 타입의 변수에 널이 될 수 없는 타입의 값을 대입은 가능.
- 널이 될 수 없는 타입의 변수에 널이 될 수 있는 타입의 변수는 대입 불가능.
- 날이 될 수 있는 타입은 원래 타입(물음표 붙이기 전)에 들 어있는 어떤 프로퍼티나 메서드를 사용할 수 없다.
- 스마트 캐스트는 날이 될 수있는 값을 처리하는 기능을 한다.
- null에 대한 검사를 한 뒤의 코드는 확실하게 null이 될 수 없다는 정보를 가지고 있기에, 널이 될 수 있는 값을 널이 될 수 없는 값으로 타입 변환한다.
- 스마트캐스트를 실행하기 위한 조건은 대상 변수의 값이 검사 지점과 사용 지점 사이에서 변하지 않는다고 컴파일러가 확실한 수 있어야 하기에 중간에 값이 변경되는 경우에는 스마트캐스트가 작동하지 않는다.
- 가변(var)프로퍼티에는 절대 스마트캐스트를 적용할 수 없다.
- 안전한 호출 연산자
- 뒤에 물음표(?)를 붙이면 된다.
- 수신 객체가 날이 아닌 경우에는 의미 있는 일을 하고, 수신 객체가 벌일 경우에는 널을 반환함.
- 엘비스 연산자
- 형태는 ?:
- 널을 대신할 디폴트 값을 지정할 수 있다.
'Skils > Kotlin' 카테고리의 다른 글
[Kotlin] 객체(Object) (0) | 2022.09.12 |
---|---|
[Kotlin] 프로퍼티(Property) (0) | 2022.09.02 |
[Kotlin] 멤버 가시성, 내포된 클래스(inner), 지역 클래스 (0) | 2022.08.30 |
[Kotlin] 클래스, 생성자 (0) | 2022.08.29 |
[Kotlin] 예외 처리 (0) | 2022.08.27 |