String? 타입의 isNullOrBlank 동작 | !연산자와 .not()의 사용

TRASALBY·2023년 4월 21일
0

Kotlin

목록 보기
1/2

문제 발견

개발을 진행하다가 예상치못한 에러를 발견했다.
String? 타입인 query와 String타입을 매개변수로 받는 getBookList() 매서드를 사용하기 위해 조건문을 사용해서 query의 타입을 확인하는 작업을 수행하던 중이었다.

	if(query != null && query.isNotBlank()){
    	mainViewModel.getBookList(query)
        }

이렇게 코드를 작성하니 ide에서 isNullOrBlank를 사용하는 것을 추천하길래 alt + enter를 사용해 변경해보았다.

	if(!query.isNullOrBlank(){
    	mainViewModel.getBookList(query)
        }

나는 !의 사용이 가독성이 좋지 않다고 생각하기에 같은 동작을 하는 .not()으로 수정해 주었다.

	if(query.isNullOrBlank().not(){
    	mainViewModel.getBookList(query)
        }

그랬더니 문제가 발생헀다.... 바로 이전까지만 해도 문제가 없었는데 .not()으로 변경하고 나니 스마트 캐스트가 진행되지 않는 것이었다!

!와 .not()의 동작 확인

우선!와 .not()의 동작에 대해 확인해 보았다.

!연산자

!연산자는 불리언 값에 직접 적용되는 값이다. 불리언 값을 반대로 뒤집는 역할을 하는데 예를 들어 'val x = true'일 때에 !x 는 false의 값이 된다.

.not() 메서드

.not()메서드는 불리언 값을 가진 객체의 메서드이다. 마찬가지로 'val x = true'일 때에 x.not()은 false가 된다.

기능적으론 같다.

결국 두 연산은 기능적으로 같은 동작을 하는 것이었다. .not()연산자는 불리언 값을 가진 객체에서만 사용되기 때문에 !을 사용하는 것이 더 편리할 수도 있다고 한다.

isNullOrBlank

그렇다면 isNullOrBlank의 수행과정에서 뭔가 차이가 있는것일까 생각이 들었다.
isNullOrBlank를 찾아 들어가 보았다.

처음보는 contract라는것을 발견하여 관련해서 구글링을 해보았다.

contract

contract는 함수의 호출을 최적화 할 수 있도록 도와주는 기능이라고 한다.
'returns(false) implies (this@isNullOrBlank != null)'
이 문장은 이 함수가 false를 반환할 때 this(그러니까 isNullOrBlank를 사용한 문자열) 은 null이 아니다 라는 정보를 같이 넘겨준다는 것이다. 이것을 통해 스마트 캐스트가 수행될 수 있는 것이다.

결론

확실한 결론은 아니지만 앞선 탐색 과정으로 부터 추론한 이유는 다음과 같다.
String?타입의 query가 가질 수 있는 값의 경우는 3가지 이다. null이거나 빈 문자열이거나 값이 있는 문자열 이거나. 만약 isNullOrBlank의 수행을 통해 false를 반환 받았다면 query는 null이 아니라는 것이다. 이 정보를 가진상태로 isNullOrBlank가 종료된다. 이상태에서 !를 사용하면 단순히 반환된 boolean의 값을 뒤집기만 하기 때문에 String 타입으로의 스마트캐스트는 유지된다. 그러나 .not()을 사용할 경우 반환된 false객체를 뒤집게 되면서 query의 값에 대한 스마트캐스트 정보를 잃게 되는것 같다. 그렇기에 query의 타입이 여전히 String?으로 남게 되고 아래 메서드에서 에러가 발생하는 것으로 생각된다.

0개의 댓글