이 포스팅은 Kotlin in Action, 드미트리 제메로프 & 스베트라나 이사코바, 에이콘출판사(2017)을 읽고 개인 학습용으로 정리한 글입니다.
어떤 타입이든 타입 이름 뒤에 물음표를 붙이면: 그 타입의 변수나 프로퍼티에 null참조를 저장할 수 있음
물음표가 없는 타입: 그 변수가 null 참조를 저장할 수 X
-> *모든 타입 기본적으로 널이 될 수 없음
널이 될 수 있는 타입인 변수에 대해 변수.메서드()처럼 메서드 직접 호출 X
널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 저장 X
널이 될 수 있는 값을 널이 될 수 없는 타입의 파라미터를 받는 함수에 전달 X
null 검사를 추가해야 코드가 컴파일된다
null 검사와 메서드 호출을 한 번의 연산으로 수행
-> 호출하려는 값이 null이 아니면: 일반 메서드처럼 호출
-> 호출하려는 값이 null이면: 호출 무시, null이 결과 값
안전한 호출의 결과 타입: 널이 될 수 있는 타입
null 대신 사용할 디폴트 값을 지정할 때 편리
이항 연산자로 좌항을 계산한 값이 null인지 검사
-> 좌항 값이 null이 아니면: 좌합 값이 결과 값
-> 좌항 값이 null이면: 우항 값이 결과 값
코틀린에서는 return이나 throw등의 연산: 식
-> 엘비스 연산자의 우항에 넣을 수 있음
-> 함수의 전제조건(precondition)검사에 유용
as?연산자는 대산 값을 지정한 타입으로 바꿀 수 없으면 null 반환
안전한 캐스트를 사용할 때 일반적인 패턴: 캐스트 수행 뒤 엘비스 연산자 사용
널 아님 단언(not null assertion): 어떤 값이든 널이 될 수 없는 타입으로 (강제로) 바꿈
-> 실제 null에 대해 !! 적용하면 NPE 발생
!!를 null에 대해 사용해서 발생하는 예외의 스택 트레이스(stack trace)
-> 어떤 파일의 몇 번째 줄인지에 대한 정보 O
-> 어떤 식에서 예외가 발생했는지에 대한 정보 X
-> 어떤 값이 null이었는지 확실히 하기 위해 여러 !! 단언문 한 줄에 쓰지 X
let 함수는 자신의 수신 객체를 인자로 전달받은 함다에게 넘김
안전한 호출 구문을 사용해 let 호출 & 널이 될 수 없는 타입을 인자로 받는 람다 let에 전달
-> 수신 객체 null이 아닌 경우: 널이 될 수 없는 타입의 값으로 람다에 전달
-> 수신 객체 null인 경우: 아무 일도 일어나지 않는다
코틀린에서는 일반적으로 생성자에서 모든 프로퍼티 초기화해야
프로퍼티 타입이 널이 될 수 없는 타입이라면 반드시 널이 아닌 값으로 초기화해야
lateinit변경자를 붙이면 프로퍼티 나중에 포기화 가능
⭐나중에 초기화하는 프로퍼티는 항상 var이어야 함
(val 프로퍼티는 final 필드로 컴파일됨 -> 생성자 안에서 반드시 초기화해야)
나중에 초기화하는 프로퍼티를 초기화하기 전에 접근하면
-> lateinit property has not been initialized 예외 발생
⚡일반 멤버 호출은 객체 인스턴스를 통해 디스패치 됨 -> 그 인스턴스가 널인지 여부 검사 X
동적 디스패치: 객체의 동적 타입에 따라 적절한 방식을 호출해주는 방식
정적 디스패치: 컴파일러가 컴파일 시점에 어떤 메서드가 호출될지 결정하는 방식
null이 될 수 있는 타입에 대해 확장 함수 정의하면
-> 널이 될 수 있는 값에 대해 안전한 호출 없이도 호출 가능
-> 그 확장 함수 내부에서 this는 널이 될 수 있음 -> 명시적으로 널 여부 검사
자바에서 메서드 안의 this는 항상 널이 아니지만, 코틀린에서는 가능함
예. String? 타입의 수신객체에 대해 호출할 수 있는 isNullOrEmpty, isNullOrBlank 메서드
코틀린에서는 함수나 클래스의 모든 타입 파라미터 기본적으로 널 가능
-> 타입 파라미터 T 이름 끝에 ?가 없더라도 T는 널이 될 수 있는 타입
타입 파라미터가 널이 아님을 확실히 하려면 널이 될 수 없는 타입 상한(upper bound) 지정해야
fun <T> printHashCode(t:T){
println(t?.hashCode()) //T의 타입 Any?로 추론됨
}
fun <T:Any> printHashCode(t:T){
println(t.hashCode())
}
코틀린이 널 관련 정보를 알 수 없는 타입
-> 널이 될 수 있는 타입으로 처리해도 되고 널이 될 수 없는 타입으로 처리해도 됨
-> 컴파일러는 모든 연산 허용
!표기: 타입의 널 가능성에 대해 아무 정보 없다는 의미
코틀린에서 플랫폼 타입 선언 X
자바에서 가져온 타입만 플랫폼 타입
공개 가시성인 코틀린 함수의 널이 아닌 타입의 파라미터와 수신 객체
-> 코틀린 컴파일러가 널 검사 추가해줌
-> 함수 호출 시점에 검사
-> 널 값을 사용하면 즉시 예외 발생
자바 클래스나 인터페이스를 코틀린에서 구현할 경우 널 가능성 처리 중요
코틀린 컴파일러는 구현 메서드의 널이 될 수 없는 타입으로 선언한 모든 파라미터에 대해 널 아님을 검사하는 단언문 만들어줌
자바는 원시 타입과 참조 타입 구분
⚡원시 타입 값에 대해 메서드 호출 X
컬렉션에 원시 타입 값 담을 수 X
-> 참조 타입이 필요한 경우 특별한 래퍼 타입으로 원시 타입 값 감싸서 사용
코틀린은 원시 타입과 래퍼 타입 구분 X, 항상 같은 타입 사용
-> 원시 타입 값에 대해 메서드 호출 O
null은 자바의 참조 타입 변수에만 대입 가능
-> 널이 될 수 있는 코틀린 타입은 자바 원시 타입으로 표현 불가능
-> 자바의 래퍼 타입으로 컴파일됨
자바나 코틀린 모두 제네릭 클래스는 박스 타입 사용
-> 타입 인자로 원시 타입 넘기면 그 타입에 대한 박스 타입(래퍼 타입)사용
코틀린은 한 타입의 숫자를 다른 타입의 숫자로 자동 변환 X
-> 직접 변환 메서드 호출해야
코틀린은 모든 원시 타입(Boolean 제외)에 대한 변환 함수 제공
⭐두 박스 타입 간의 equals 메서드는 그 안에 들어있는 값이 아니라 박스 타입 객체를 비교
숫자 리터럴을 사용할 때는 변환 함수 호출 필요 X
-> 상수 뒤에 타입을 표현하는 문자 붙이기 (ex. 42L, 42.0f)
-> 타입이 알려진 변수에 대입하면 컴파일러가 자동으로 변환
자바 Object: 클래스 계층(참조 타입만 포함)의 최상위 타입
코틀린 Any: 모든(원시 타입 포함) 널이 될 수 없는 타입의 조상 타입
코틀린에서 널을 포함하는 모든 값을 대입할 변수를 선언하려면 Any? 타입 사용
모든 코틀린 클래스에는 toString, equals, hashCode라는 세 메서드 들어있음
-> 이 세 메서드는 Any에 정의된 메서드를 상속한 것
코틀린의 Unit타입: 자바 void와 같은 기능
Unit은 모든 기능을 갖는 일반적인 타입
-> void와 달리 타입 인자로 쓸 수 O
Unit 타입에 속한 값 하나 뿐, 그 이름도 Unit
Unit 타입의 함수는 Unit 값을 묵시적으로 반환
-> 컴파일러가 묵시적으로 return Unit 넣어줌
결코 성공적으로 값을 돌려주는 일이 없으므로 '반환 값' 개념 자체가 없는 의미 없는 함수 존재
Nothing 타입은 아무 값도 포함하지 X
-> 함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 사용 가능
-> Nothing 타입의 변수를 선언하더라도 아무 값도 저장할 수 X
코틀린에서는 컬렉션의 데이터를 접근하는 인터페이스와 컬렉션 안의 데이터를 변경하는 인터페이스 분리
kotlin.collections.MutableCollection은 kotlin.collections.Collection을 확장
방어적 복사(defensive copy): 원본의 변경을 막기 위해 변경 가능한 컬렉션을 읽기 전용 컬렉션으로 복사하여 전달
읽기 전용 컬렉션이 꼭 변경 불가능한 컬렉션일 필요 X
-> 읽기 전용 인터페이스 타입인 변수를 사용할 때
그 인터페이스는 실제로는 어떤 컬렉션 인스턴스를 가리키는 수많은 참조 중 하나일 수 있음
읽기 전용 컬렉션이 항상 스레드 안전하지는 않다
자바는 읽기 전용 컬렉션과 변경 가능 컬렉션 구분 X
-> 코틀린에서 읽기 전용 컬렉션으로 선언된 객체라도 자바 코드에서는 내용 변경 가능
컬렉션을 변경하는 자바 메서드에 읽기 전용 컬렉션을 넘겨도 컴파일러가 막을 수 X
컬렉션에 null을 넣는 자바 메서드에 널이 아닌 원소로 이루어진 컬렉션을 넘겨도 컴파일러가 막을 수 X
기본적으로는 배열보다 컬렉션을 더 먼저 사용해야
컬렉션 -> 배열 변환: toTypedArray 매서드 사용
코틀린에서 배열을 만드는 방법:
코틀린은 원시 타입의 배열을 표현하는 별도의 클래스를 워시 타입마다 하나씩 제공
(ex. IntArray, ByteArray, ...)
-> 자바의 원시 타입 배열로 컴파일됨 (int[], byte[], ...)
-> 배열의 값 박싱되지 X
코틀린 표준 라이브러리는 배열에 배열 기본 연산 + 컬렉션에 사용할 수 있는 모든 확장 함수 제공