옵셔널 개념이 나오기 전까지, 자바 생태계에서는 참조하는 대상이 Null이 아니라고 간주하였다.
따라서 모든 참조에 대해서 Null을 체크해야하는 불필요한 관습이 정착되었고
이를 간소화하기 위해 @Nullable이나 @NonNull, @NotNullable 등의 어노테이션을 사용하기도 하였다.
이후 자바8에서 옵셔널 개념이 도입되었으나, 근본적으로 Null 여부를 체크해야하는 것은 변함없다.
그렇다면 코틀린은 어떻게 처리할까?
코틀린은 Null을 그대로 포용한다.
대신 Null로 초기화될 수 있는 타입과 초기화될 수 없는 타입을 분류하여 처리한다.
참고 코틀린의 Null 체크는 완벽한가? 예를 들어 Map<K, V>.get(key)는 key에 해당하는 값이 없으면 null을 반환해주지만 List<T>.get(index)는 index에 해당하는 값이 없으면 IndexOutOfBoundsException을 던진다. 유사하게 Iterable<T>.first()는 null 대신 NoSuchElementException을 던진다. 사실 이것은 Null 체크 때문이 아니라 보다 안전한 코딩을 위해 코틀린의 구현체가 다르기때문에 발생한다.
object Legs { @JvmStatic funfindLongestLegOver( legs: List<Leg>, duration: Duration, ): Optional<Leg> { var result: Leg? = null for (leg in legs) { if (isLongerThan(leg, duration)) if (result == null || isLongerThan(leg, result.plannedDuration)) result = leg } return Optional.ofNullable(result) }
object Legs { @JvmStatic funfindLongestLegOver( legs: List<Leg>, duration: Duration, ): Optional<Leg> { // 함수 추출 적용 return Optional.ofNullable(longestLegOver(legs, duration)) }
// Leg?를 반환하는 새로운 함수 funlongestLegOver( legs: List<Leg>, duration: Duration, ): Leg? { var result: Leg? = null for (leg in legs) { if (isLongerThan(leg, duration)) if (result == null || isLongerThan(leg, result.plannedDuration)) result = leg } return result }
funlongestLegOver( legs: List<Leg>, duration: Duration, ): Leg? { var result: Leg? = null for (leg in legs) { if (isLongerThan(leg, duration)) if (result == null || isLongerThan(leg, result.plannedDuration)) result = leg } return result }
funlongestLegOver( legs: List<Leg>, duration: Duration, ): Leg? { var result: Leg? = null for (leg in legs) { if (leg.isLongerThan(duration)) if (result == null || leg.isLongerThan(result.plannedDuration)) result = leg } return result }