- 코틀린은 null-safety 매커니즘으로 인해 NPE를 거의 찾아보기 힘듬
- null-safety 매커니즘이 없는 자바, C 등의 프로그래밍 언어와 코틀린을 연결해서 사용할 때는 NPE 예외가 발생할 수 있음
public class JavaTest{
public String giveName() { ... }
}
- 위 자바 코드로 반환된 타입을 사용할때에 @Nullable 어노테이션이 붙어 있다면 nullable로 추정하고 String?으로 변경하면 되는데 만약 붙어 있지 않다면 자바에서 모든 것이 nullable일 수 있으므로 최대한 안전하게 접근하기 위해 nullable로 가정하고 접근해야 함
제네릭 타입
public class UserRepo {
public List<User> getUsers() { ...}
}
val users: List<User> = UserRepo().users!!.filterNotNull()
- 코틀린이 디폴트로 모든 타입을 nullable로 다룬다면, 이를 사용할 때 이러한 리스트와 리스트 내부의 User 객체들이 널 아니라는 것을 알아야 함
- 그래서 코틀린은 자바 등의 다른 프로그래밍 언어에서 넘어온 타입들을 특수하게 다루고 이러한 타입을 플랫폼 타입이라고 부름
val repo = UserRepo()
val user1 = repo.user // user1의 타입 User!
val user2: User = repo.user // User
val user3: User? = repo // User?
val users: List<User> = UserRepo().users
val users: List<List<User>> = UserRepo().groupedUsers
- 코틀린에서는 플랫폼 타입은 타입 뒤에 ! 기호를 붙여서 표기함
- 그러나 문제는 null이 아니라고 생각되는 것이 null일 가능성이 있으므로 여전히 위험하기 때문에 항상 주의를 기울여야 하고 설계자가 명시적으로 어노테이션으로 표기하거나 주석으로 달아두어야함
어노테이션 지원 목록
- JetBrains의 @Nullable @NotNull
- 안드로이드 @Nullable @NonNull
- JSR-305( @Nullable, @CheckForNull @Nonnull
- JavaX( @Nullable@CheckForNull@Nonnulljavax.annotation
- FindBugs( @Nullable, @CheckForNull, @PossiblyNull @NonNull
- ReactiveX( @Nullable @NonNull
- 이클립스 ( @Nullable @NonNull
- 롬복 ( @NonNull
- 또는 JSR 305의 @ParametersAreNonnullByDefault주석을 사용하여 기본적으로 모든 유형이 Notnull이어야 함을 Java에서 지정할 수 있습니다 .
플랫폼 타입 사용의 문제점
public class JavaClass {
public String getValue() {
return null;
}
}
fun staredType() {
val value: String = JavaClass().value
println(value.length)
}
fun platformType() {
val value = JavaClass().value
println(value.length)
}
- 코틀린에서도 위와 같이 플랫폼 코드를 사용할 수 있으나 플랫폼 타입은 안전하지 않으므로 빨리 제거하는 것이 좋음
- 위 stratedType, platformType 모두 null을 리턴한다고 가정하지 않으면 NPE가 발생
- startedType의 경우 자바에서 가져오는 값을 가져오는 위치에서 NPE 발생
- null이 아니라고 예상했지만 null이 나와 가져오는 지점에서 발생
- platformType은 값을 활용할때 NPE 발생
- 플랫폼 타입으로 지정된 변수는 nullable일 수도 있고, 아닐 수도 있어서 실제로 활용하는 라인에서 발생
- startedType의 경우 자바에서 가져오는 값을 가져오는 위치에서 NPE 발생
interface UserRepo {
fun getUserName() = JavaClass().value
}
class UserRepoImp: UserRepo {
override fun getUserName():String? {
return null
}
}
fun main() {
val repo: UserRepo = UserRepoImp()
val text: String = repo.getUserName()
println("User name length is ${text.length}")
}
- 위 인터페이스 메소드의 inferred 타입이 플랫폼 타입이므로 누구나 nullable여부를 지정할 수 있는데 사용하는 쪽에서 nullable이 아니라고 받아들였다면 문제가 발생
- 즉 플랫폼 타입이 전파되는 일은 굉장히 위험하고 가급적 제거하는 것이 좋음
정리
- 다른 프로그래밍 언어에서 와서 nullable 여부를 알 수 없는 타입을 플랫폼 타입이라고 함
- 플랫폼 타입은 사용하는 코드말고도 활용하는 곳까지 영향을 줄 수 있으므로 가급적 제거하는 것이 좋음
- 혹은 사용하게 되었을때는 nullable 여부를 지정하는 어노테이션을 활용하는 것이 좋음
'Reading Record > 이펙티브 코틀린' 카테고리의 다른 글
[이펙티브 코틀린] Item6. 사용자 정의 오류보다는 표준 오류를 사용하라 (0) | 2022.04.06 |
---|---|
[이펙티브 코틀린] Item5. 예외를 활용해 코드에 제한을 걸어라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item4. inferred 타입으로 리턴하지 말라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item 2. 변수의 스코프를 최소화하라 (0) | 2022.04.06 |
[이펙티브 코틀린] Item1. 가변성을 제한하라 (0) | 2022.04.06 |