Q: 자바에서 String 타입을 리턴하는 메서드가 있다고 가정 코틀린에선 어떻게 처리?
A:
@Nullable 어노테이션이 붙어 있다면, 이를 nullable로 추정 -> String?으로 변경
@NotNull 어노테이션이 붙어 있다면 -> String으로 변경
If 다음과 같이 어노테이션이 붙어 있지 않다면?
//자바
public class JavaTest {
public String giveName() {
//...
}
}
자바에서는 모든 것이 nullable일 수 있으므로 최대한 안전하게 접근한다면
-> 이를 nullable로 가정하고 다뤄야 함
🔥하지만, 어떤 메서드에서는 null을 리턴하지 않을 것이 확실하다면
-> 마지막에 not-null 단정을 나타내는 !!을 붙임
⚡ nullable과 관련하여 자주 문제가 되는 부분은 자바의 제네릭 타입
List<User>
를 리턴하고, 어노테이션이 따로 붙어 있지 않은 경우엔??코틀린이 디폴트로 모든 타입을 nullable로 다룬다면
-> 이를 사용할 때 리스트와 리스트 내부의 User 객체들이 널이 아니라는 것을 알아야 함
//자바 public class UserRepo { public List<User> getUsers() { //... } } //코틀린 val users: List<User> = UserRepo().users!!.filterNotNull()
↪ 함수가 List<List<User>>
를 리턴한다면 ? -> 복잡해짐..
val users: List<List<User>> = UserRepo().groupUsers!!
.map { it!!.fillterNotNull() }
리스트는 적어도 map와 filterNotNull 등의 메서드를 제공 !
다른 제네릭 타입이라면, 널을 확인하는 것 자체가 복잡한 일이 됨 ...
그래서 코틀린은 다른 프로그래밍 언어에서 넘어온 타입들을 특수하게 다룸
-> 이러한 타입을 플랫폼 타입(platform type)
플랫폼 타입이란, 다른 프로그래밍 언어에서 전달되어서 nullable인지 아닌지 알 수 없는 타입을 말합니다.
이러한 노테이션이 직접적으로 코드에 나타나진 ❌
대신 다음 코드와 같은 형태로 이를 선택적으로 사용 !
//자바 public class UserRepo { public User getUser() { //... } } //... //코틀린 val repo = UserRepo() val user1 = repo.user // user1의 타입은 User! val user2: User = repo.user // user2의 타입은 User val user3: User? = repo.user // user3의 타입은 User?
이러한 코드를 사용할 수 있으므로, 이전에 언급된 문제가 사라짐
val users: List<User> = UserRepo().users
val users: List<List<User>> = UserRepo().groupedUsers
문제는 null이 아니라고 생각했던 것이 null일 가능성이므로, 여전히 위험💣
함수가 지금 당장 null을 return하지 않아도, 미래에는 변경될 수 있다는점 인지!
자바와 코틀린을 함께 사용중일 때,
자바 코드를 직접 조작할 수 있다면 가능한 @Nullable과 @NotNull 어노테이션 붙여서 사용권장🔥
// 자바
public class JavaClass {
public String getValue() {
return null;
}
}
// 코틀린
fun statedType() {
val value: String = JavaClass().value
//...
println(value.length)
}
fun platformType() {
val value = JavaClass().value
//...
println(value.length)
}
두 가지 모두 NPE(Null Pointer Exception)가 발생
두 코드는 오류의 발생 위치에 차이 존재
statedType에서는 자바에서 값을 가져오는 위치에서 NPE가 발생
오류가 발생 하면, null이 아니라고 예상을 했지만 null이 나옴
platformType에서는 값을 활용할 때 NPE가 발생
// 자바
public class JavaClass {
public String getValue() {
return null;
}
}
// 코틀린
fun statedType() {
val value: String = JavaClass().value // NPE
//...
println(value.length)
}
fun platformType() {
val value = JavaClass().value
//...
println(value.length) // NPE
}
interface UserRepo { fun getUserName() = JavaClass().value }
↪ 메서드의 inferred 타입(추론된 타입)이 플랫폼 타입
이는 누구나 nullable 여부를 지정할 수 있음
ex) 어떤 사람이 이를 활용해서 nullable을 리턴하게 했는데, 사용하는 사람이 nullable이 아닐거라고 받아들였다면 문제됨...
class RepoImpl: UserRepo { override fun getUserName(): String? { return null } } fun main() { val repo: UserRepo = RepoImpl() val text: String = repo.getUserName() // 런타임 때 NPE print("User name length is ${text.legth}") }
다른 프로그래밍 언어에서 와서 nullable 여부를 알 수 없는 타입을 플랫폼 타입이라고 부름
이러한 플랫폼 타입을 사용하는 코드는 위험한 코드💣
-> 빨리 제거 필요
➕ 연결되어 있는 자바 생성자, 메서드, 필드에 nullable 여부를 지정하는 어노테이션을 활용하는 것도 좋음👍