본문 바로가기

카테고리 없음

null 안정성과 플랫폼 타입

null-safety는 코틀린의 주요 기능 중 하나 입니다 자바에서 자주 볼 수 있는 null-pointer Exception은 코틀린에서 null-safety 매커니즘으로 찾아볼 수 없습니다.

 

null-safety가 없는 자바에서 string 타입을 리턴하는 매서드가 있다고 한다면 코틀린에서 이를 사용하려면 @nullable 어노테이션이 붙어 있다면 nullable로 추정하고, string?로 변경 하면 됩니다

 

@nullable 어노테이션이 붙어있지 않다면 모든것이 nullable일 수 있기에 안전하게 접근하기 위해 이를 nullable로 가정하고 다루어야 합니다. 하지만 null이 리턴하지 않는게 확실한 부분이 있을수 있기에 그럴 경우 !!를 붙입니다

 

nullable과 관련하여 나타나는 분제가 자바의 제너릭 타입입니다 자바의 API에서 list<user>를 리턴하고 어노테이션이 붙어 있지 않을 경우 user의 객체가 null이 아니라는 것과 내부의 값들도 null유무를 확인해야 합니다.

 

public class UserRepo {
    public List<User> getUsers(){
        // ***
    }
}
val users:List<User> = UserRepo().users!!.filterNoNull()

 

만약 함수가 List<List<User>>를 리턴한다면 훨씬 더 복잡해 집니다.

val users:List<List<User>> = UserRepo().groupedUsers!!.map{it!!.filterNotNull()}
}

List는 적어도 filterNotNull과 map등의 매서드를 제공 하지만 다른 제너릭 타임이라면 null을 확인하는게 정말 어려워 집니다 그래서 다른 언어에서 넘어온 타입들을 특수하게 다루는데 이러한 타입을 플랫폼 타입이라 부릅니다

 

플랫폼 타임은 string처럼 이름 뒤에 !를 붙여서 표기합니다 물론 이러한 노테이션을 직접적으로 표기하지는 않습니다. 대신 다음 코드 형태로 선택적으로 사용합니다.

public class UserRepo {
    public User getUser(){
        // ***
    }
}
val repo = UserRepo()
val user1 = repo.user
val user2: User = repo.user
val user3: User? = repo.user

 

이러한 코드를 사용하므로 이전에 언급한 문제가 사라집니다. 하지만 null이 아니라고 생각되는 것이 null일 수도 있어서 여전히 위험합니다. 어노테이션으로 표시하거나 주석을 사용자가 달아두지 않으면 언제든 동적으로 변할 수 있습니다 변경 될 수도 있다는 것을 항상 염두해 두어야 합니다.

 

자바를 코틀린가 붙여서 사용할 때는 가능한 @Nullable과 @NotNull을 붙여 사용합시다.

import org.jetbrains.annotations.NotNull;

public class UserRepo {
    public @NotNull User getUser(){
        // ...
    }
}

이는 코틀린 개발자를 지원하고 싶을 경우, 가장 중요한 단계입니다.

코틀린 언어로 메인 언어를 변경 했을때 가장 중요한 사항으로 어노테이션들이 언급되고 있습니다.

 

지원중인 어노테이션

  • JetBrains(org.jetbrains.annotations의 @Nullable,@NotNull)
  • Anroid(androidx.annotation,com.android.annotations,android.support.annotations의 @Nullable,@NonNull)
  • JSR-305(javax.annotation의 @Nullable,@NonNull,@CheckForNull)
  • JavaX(javax.annotation의 @Nullable,@NonNull, @CheckForNull)
  • FindBugs(edu.umd.cs.findbugs.annotations의 @Nullable,@NonNull, @CheckForNull,@PossiblyNull)
  • ReactiveX(io.reactivex.annotations의 @Nullable,@NonNull)
  • Eclipse(org.eclipse.jpt.annotation의 @Nullable,@NonNull)
  • Lombok(Lombok의 NonNull)

코틀린 코드에서도 사용할 수 있습니다 하지만 안전하지 않으므로 최대한 빨리 제거하는게 좋습니다

간단한 statedType과 platformType 동작 예시 입니다


public class javaClass {
    public String getValue()
    {
        return null
    }
}

fun statedType() {
    val value: String = javaClass().value
    // ...
    println(value.length)
}

fun platfoemType() {
    val value = javaClass().value
    // ...
    println(value.length)
}

두 가지 모두 (NPE)가 발생합니다. 일반적으로 getValue가 null을 리턴할 거라고 가정하지 않으므로 실수 했다고 생각 할 겁니다 두 코드는 오류 발생 위치에 차이가 있습니다.

 

statedType에서는 자바에서 값을 가져오는 위치에서 NPE가 발생 합니다.  null이 아니라고 예상 했지만 null이 나온다는 것을 쉽게 파악할수 있습니다

 

platformType에서는 값을 활용할 때 NPE가 발생합니다.  실제로는 복잡한 표현식에 이런 문제가 발생 합니다. 플래폼으로 지정된 변수는 nullable일 수도 있고 아닐수도 있습니다. 이러한 변수를 안전하게 사용하더라도 다른 사용자가 사용시 NPE를 발생 시킬수 있기에 오류 발생시 찾는데 시간이 많이 소요 됩니다.

 

플랫폼 타임 정리 : 다른 프로그래밍 언어에서 와서 nullable 여부를 알 수 없는 타입을 플랫폼 타입이라고 부릅니다. 이러한 플랫폼 타입은 이를 활용하는 곳 까지 영향을 줄 수 있기에 제거하는 것이 좋습니다 또한 연결되어 있는 자바 생성자, 메서드,필드에 nullable 여부를 지정하는 어노테이션을 활용하는 것도 좋습니다.

 

728x90