이펙티브 코틀린 item3: 최대한 플랫폼 타입을 사용하지 말라

woga·2023년 2월 12일
0

코틀린 공부

목록 보기
7/54
post-thumbnail

코틀린의 널 안정성은 코틀린의 주요 기능 중 하나이다. 이 기능 덕분에 코틀린을 쓰면 NPE가 날 일이 없는데, 자바, C 등의 프로그래밍 언어와 코틀린을 같이 쓸 땐 이 예외가 발생할 수 있다.

플랫폼 타입?

Q) 그럼 우린 이를 방지할 수 있나?

있긴 하다! 바로 어노테이션을 사용하는건데 @Nullable , @NotNull, @NonNull (논널인지 낫널인지 sdk 따라 다르다 본인이 사용하는 플랫폼에 따라 적절하게 쓰면 된다)로 표시해두면 객채에 ? 를 붙일 지 말지 결정할 수 있다

그래도 자주 문제가 되는 부분이 있는데 바로 자바의 제네릭 타입이다.
자바 API에서 List<User>를 리턴하고, 어노테이션이 따로 붙어 있지 않은 경우를 생각해보자. 코틀린이 디폴트로 모든 타입을 nullable로 다룬다면, 우리는 이를 사용할 때 이러한 리스트와 리스트 내부의 User 객체들이 널이 아니란 것을 알아야 한다. 따라서 리스트 자체만 널인지 확인해서는 안 되고, 내부에 있는 것들도 널인지 확인해야 한다.

(만약 List<List<User>> 라면 더 복잡해진다!)

하지만 리스트는 map이나 filterNotNull 등의 메서드가 있기라도 하지, 다른 제네릭 타입이라면 널 확인하는게 힘들어진다.

그래서 코틀린은 자바 등의 다른 프로그래밍 언어에서 넘어온 타입들을 특수하게 다루는데, 이러한 타입을 플랫폼 타입이라고 한다.

관련해서 이를 선택적으로 사용할 수 있다.

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?

그러나...

문제는 -> null 아니라고 생각되는 것이 null일 케이스다

그래서 여전히 위험하기에 플랫폼 타입을 사용할 때는 항상 주의를 기울여야 한다. 어노테이션이나 주석이 따로 없으면 동작이 변경될 가능성이 있기에 항상 잘 명시해주자

함수가 지금 당장 null을 리턴하지 않아도, 미래에는 변경될 수도 있다는 것을 염두해야 한다.

물론 제일 좋은 점은 플랫폼 타입은 안전하지 않으므로 최대한 빨리 제거하는 것이 제일 좋다. 왜냐하면 한 두번 플랫폼 타입 변수를 안전하게 사용했더라도, 이후에 다른 사람이 사용할 때는 NPE를 발생시킬 가능성이 존재하다. 그런데 이 문제를 검사기도 검출할 수 없고 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
}

이처럼 많은 위험성을 갖고 있으므로 안전한 코드를 원한다면 제거하는 것이 제일 Best다!
제거가 어려우면 최소한이라도 어노테이션을 잘 활용하는 것이 좋다.

마치며

어노테이션을 사실 잘 적어야한다는 걸 알면서도 개발하다보면 잊기 마련인데, 이 파트는 그런 내게 경각심을 불러일으켰다.. 아직 서비스에서 자바 클래스도 있기 때문에 유심히 잘.. 변경해야겠다!

profile
와니와니와니와니 당근당근

0개의 댓글