hyeals study
[코틀린] 타입 시스템 (1) - 널 가능성 본문
※ 코틀린의 타입 시스템(type system)은 자바와 비교해서 코드의 가독성을 향상시키는데 도움을 주는 몇 가지 특성이 있음.
- 널이 될 수 있는 타입(nullable type)
- 읽기 전용 컬렉션
- 배열 지원
[널 가능성(nullability)]
: NullPointerException오류를 피할 수 있게 도움을 주는 코틀린 타입 시스템을 특성
☞ 널이 될 수 있는지의 여부를 타입 시스템에 추가함으로써 컴파일러가 컴파일 시 오류를 미리 감지해서 실행 시점에 발생할 수 있는 예외 가능성을 줄일 수 있음.
[널이 될 수 있는 타입]
* 코틀린과 자바의 가장 중요한 차이: 코틀린 타입 시스템이 널이 될 수 있는 타입을 명시적으로 지원하는 것.
* 널이 될 수 있는 타입: 어떤 프로퍼티나 변수에 null을 허용.
→ 어떤 변수가 null이 될 수 있다면 그 변수에 대해 메소드를 호출하면 NullPointerException이 발생할 수 있어서 안전하지 않음. 따라서 코틀린은 이런 메소드 호출을 금지함으로써 오류를 방지함.
자바를 사용한 아래의 코드로 예를 들면, 이 함수에 null을 넘기면 NullPointerException이 발생하기 때문에 안전하지 않음.
이를 코틀린에서 다시 작성해보면 아래와 같음. 널이 인자로 들어올 수 없다면 아래와 같이 함수를 정의할 수 있음.
- 위의 코드에서 strLen에 null이거나 널이 될 수 있는 인자를 넘기는 것은 금지이며 만약 이러한 값을 넘기게 되면 컴파일 시 오류가 발생함. (바로 아래에 이 결과를 확인할 수 있게끔 예시를 적어둠)
- 위의 에러 설명: 이 코드에서 s의 타입은 항상 String의 인스턴스이어야 하는데 널을 넣었기 때문에 에러가 발생.
☞ 위아 같이 코틀린의 컴파일러가 널이 될 수 있는 값을 인자로 넘기지 못하게 막기 때문에 NullPointerException가 실행 시점에서 발생하지 않음을 장담해줌.
* 앞서 본 예시가 널 & 문자열을 인자로 받을 수 있게끔 하기 위해서는 타입 이름 뒤에 물음표(?)를 명시하면 됨.
- 위의 코드처럼 어떤 타입이든 타입 이름 뒤에 ?를 붙이면 그 타입의 변수나 프로퍼티에 널 참조를 저장할 수 있다는 뜻임.
* 하지만 널이 될 수 있는 타입의 변수가 있으면 그에 대해 수행할 수 있는 연산이 제한됨.
예를 들어, 아래의 코드에서 변수는 널이 될 수 있는 타입이며, 변수.메소드() 처럼 메소드를 직접 호출할 수 없음.
또한,
- 널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없음
- 널이 될 수 있는 타입의 값을 널이 될 수 없는 타입의 파라미터를 받는 함수에 전달 할 수 없음
<널이 될 수 있는 타입의 장점>
☞ 널이 될 수 있는 타입과 널이 될 수 없는 타입을 구분하면 각 타입의 값에 대해 어떤 연산이 가능할지 명확히 이해할 수 있으며, 실행 시점에 예외를 발생시킬 수 있는 연산을 판단할 수 있음.
[호출 연산자 ?.]
* ?.: 호출연산자이며, null 검사와 메소드 호출을 한 번의 연산으로 수행함.
→ 예를 들어, s?.toUpperCase()는 if (s!=null) s.toUpperCase() else null 과 같음
☞ 따라서 ?.은 호출하려는 값이 null 이 아니면 일반 메소드 호출처럼 작동함. (호출하려는 값이 null 이면 무시되고 null이 결과값이 됨)
* 메소드 호출뿐만 아니라 프로퍼티를 읽거나 쓸 때도 안전한 호출 사용 가능.
[엘비스 연산자 ?:]
* ?:는 null 대신 사용할 디폴트 값을 지정할 때 사용하는 연산자임. (엘비스 연산자(=null coalescing 연산자)라 부름)
사용 방법은 아래와 같음
- 위의 코드에서 s가 null이면 결과가 빈 문자열("")이됨 ( null 이 아니면 결과는 s)
[as? 연산자]
* as 연산자: 타입 캐스트 연산자 (https://hyeals.tistory.com/13 참조)
☞ 자바의 타입 캐스트와 마찬가지, 대상 값을 as로 지정한 타입으로 바꿀 수 없으면 ClassCastException이 발생
* as? 연산자: 어떤 값을 지정한 타입으로 캐스트. 값을 대상 타입으로 변환할 수 없는 경우 null 반환.
* 안전한 캐스트를 할 때 일반적인 패턴: 캐스트 수행 → 엘비스 연산자 사용
☞ 이 패턴을 사용할 경우 장점
- 파라미터로 받은 값이 원하는 타입인지 쉽게 검사하고 캐스트할 수 있음
- 타입이 맞지 않으면 쉽게 false 반환 가능
* 아래의 코드는 as? 연산자를 사용해서 equals 메소드를 구현한 예시임.
- val otherPerson = o as? Person ?: return false 부분: 타입이 일치하지 않으면 false 반환
- equals 메소드 return 부분: as? 연산자를 사용해서 안전한 캐스트를 하고 난 후 otherPerson이 Person 타입으로 스마트 캐스트 됨.
- == 연산자는 equals 메소드를 호출함
[!! 연산자]
* !! 연산자: 어떤 값이든 널이 될 수 없는 타입으로 강제로 바꿀 수 있음.
☞ 컴파일러에게 이 값은 널이 아님을 단언해 주는 것임.
[타입 파라미터 널 가능성]
: 코틀린에서 함수 or 클래스의 모든 타입 파라미터는 기본적으로 널이 될 수 있음.
* 널이 될 수 있는 타입을 포함하는 어떤 타입이라도 타입 파라미터를 대신할 수 있음.
☞ 타입 파라미터 T를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 물음표가 없어도 T가 널이 될 수 있는 타입임.
- println(t?.hashCode()) 부분: t가 널이 될 수 있으므로 ?.를 사용해서 안전한 호출을 해야 함
- printHashCode(null) 부분: T의 타입이 Any?로 추론됨.
* 만약 타입 파라미터가 널이 아님을 확실히 하고 싶으면 널이 될 수 없도록
→ "fun <T:Any> printHashCode(t:T)" 처럼 타입 상한(upper bound)을 두면 됨.
본 게시물은 "Kotlin IN ACTION"을 참고함.
'코틀린' 카테고리의 다른 글
[코틀린] 연산자 오버로딩 & 관례 - (1) 산술연산자 오버로딩 (0) | 2020.02.25 |
---|---|
[코틀린] 타입 시스템 (2) - 원시 타입 (0) | 2020.02.25 |
[코틀린] 수신 객체 지정 람다 (0) | 2020.02.22 |
[코틀린] 람다식 & 멤버 참조 (0) | 2020.02.21 |
[코틀린] object 키워드 (0) | 2020.02.19 |