'변수'는 ​​현재 변경 될 수있는 변경 가능한 속성이므로 '유형'으로 스마트 캐스트 할 수 없습니다.


275

그리고 Kotlin 초보자는 "다음 코드가 컴파일되지 않는 이유는 무엇입니까?"라고 묻습니다.

    var left: Node? = null

    fun show() {
         if (left != null) {
             queue.add(left) // ERROR HERE
         }
    }

'left'는 현재 변경 될 수있는 변경 가능한 속성이므로 'Node'로 스마트 캐스트 할 수 없습니다.

그 얻을 left가변 변수,하지만 난 명시 적으로 확인하고있어 left != nullleft유형 인 Node유형에 스마트 캐스트 왜이 될 수 없다?

이것을 어떻게 우아하게 고칠 수 있습니까? :)


3
다른 스레드 사이의 어딘가에서 값을 다시 null로 변경했을 수 있습니다. 나는 다른 질문에 대한 대답도 언급 할 것이라고 확신합니다.
nhaarman

3
안전한 전화 를 사용 하여 추가 할 수 있습니다
Whymarrh

말이되는 @nhaarman에게 감사합니다. Whymarrh는 어떻게 할 수 있습니까? 안전한 호출은 메소드가 아닌 객체에만 해당된다고 생각했습니다.
FRR

6
같은 것 : n.left?.let { queue.add(it) }나는 생각 하는가?
Jorn Vernee

답변:


358

실행 left != nullqueue.add(left)다른 스레드 사이 의 값을 left로 변경했을 수 있습니다 null.

이 문제를 해결하려면 몇 가지 옵션이 있습니다. 여기 몇 가지가 있습니다 :

  1. 스마트 캐스트와 함께 지역 변수를 사용하십시오.

    val node = left
    if (node != null) {
        queue.add(node)
    }
  2. 다음 중 하나와 같은 안전한 통화를 사용하십시오 .

    left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
  3. Elvis 연산자 를 사용하여 return둘러싸는 함수에서 일찍 복귀하십시오.

    queue.add(left ?: return)

    그 주 breakcontinue루프 내에서 검사에 유사하게 사용할 수 있습니다.


8
4. 가변 변수가 필요없는 문제에 대한보다 기능적인 해결책을 생각해보십시오.
Good Night Nerd Pride

1
@sak 그것은 Node질문의 원래 버전에서 정의 된 클래스 의 인스턴스로, n.left대신에 보다 복잡한 코드 스 니펫을 가졌다 left. 이에 따라 답변을 업데이트했습니다. 감사.
mfulton26

1
@sak 같은 개념이 적용됩니다. val각에 대해 새 항목 을 작성 하거나 var여러 ?.let명령문을 중첩 시키거나 ?: return함수에 따라 여러 명령문을 사용할 수 있습니다. 예 MyAsyncTask().execute(a1 ?: return, a2 ?: return, a3 ?: return). "다중 변수 let" 에 대한 솔루션 중 하나를 시도 할 수도 있습니다 .
mfulton26

1
@FARID 누가 말하는거야?
mfulton26

3
예, 안전합니다. 변수가 전역 클래스로 선언되면 모든 스레드가 해당 값을 수정할 수 있습니다. 그러나 로컬 변수 (함수 내에 선언 된 변수)의 경우 다른 스레드에서 해당 변수에 도달 할 수 없으므로 사용하기에 안전합니다.
Farid

31

1) 또한 사용할 수 있습니다 lateinit당신이 경우 반드시 나중에 당신의 초기화를 onCreate()하거나 다른 곳.

이것을 사용하십시오

lateinit var left: Node

이 대신

var left: Node? = null

2) 그리고 !!이와 같이 사용할 때 변수 끝 을 사용하는 다른 방법 이 있습니다

queue.add(left!!) // add !!

무엇을합니까?
c-an

@ c-an 변수를 null로 초기화하지만 나중에 코드에서 초기화해야합니다.
Radesh

그렇다면 동일하지 않습니까? @Radesh
c-an

@ c- 무엇과 동일합니까?
Radesh

1
위의 질문에 '노드'로 스마트 캐스트하는 것은 불가능합니다. '왼쪽'은 이번에 변경 될 수있는 가변 속성이기 때문에이 코드는 변수 종류를 지정하여 오류를 방지합니다. 따라서 컴파일러는 스마트 캐스트가 필요하지 않습니다
Radesh

27

mfulton26의 답변 외에도 네 번째 옵션이 있습니다.

?.연산자 를 사용하면 let로컬 변수를 처리 하거나 사용 하지 않고 필드뿐만 아니라 메소드를 호출 할 수 있습니다 .

문맥에 대한 일부 코드 :

var factory: ServerSocketFactory = SSLServerSocketFactory.getDefault();
socket = factory.createServerSocket(port)
socket.close()//smartcast impossible
socket?.close()//Smartcast possible. And works when called

메소드, 필드 및 내가 작동 시키려고했던 다른 모든 것들과 함께 작동합니다.

따라서 수동 캐스트를 사용하거나 지역 변수를 사용하는 대신 문제를 해결하기 위해 ?.메소드를 호출하는 데 사용할 수 있습니다 .

참고로 이것은 Kotlin 1.1.4-3에서 테스트되었지만 1.1.51및 에서 테스트되었습니다 1.1.60. 다른 버전에서 작동한다는 보장은 없으며 새로운 기능 일 수 있습니다.

은 Using ?.가 문제입니다 전달 된 변수이기 때문에 조작하는 것은 귀하의 경우에는 사용할 수 없습니다. Elvis 연산자는 대안으로 사용될 수 있으며 아마도 가장 적은 양의 코드가 필요한 연산자 일 것입니다. continue그래도 사용하는 대신 return사용할 수도 있습니다.

수동 캐스팅을 사용하는 것도 옵션 일 수 있지만 이것은 null 안전하지 않습니다.

queue.add(left as Node);

다른 스레드에서 왼쪽 이 변경 되면 프로그램이 중단됩니다.


내가 아는 한 '?.' 연산자는 왼쪽에있는 변수가 null인지 확인합니다. 위의 예에서는 'queue'입니다. '스마트 캐스트 불가능'오류는 "add"메소드에 전달 된 "left"매개 변수를 참조하고 있습니다.이 방법을 사용하면 여전히 오류가 발생합니다.
FRR

맞습니다. 오류는 켜져 left있지만 아닙니다 queue. 이를 확인해야합니다. 잠시 후 답변을 수정합니다.
Zoe

4

이것이 작동하지 않는 실제적인 이유는 스레드와 관련이 없습니다. 요점은 node.left로 효과적으로 번역 된다는 것 입니다 node.getLeft().

이 속성 getter는 다음과 같이 정의 될 수 있습니다.

val left get() = if (Math.random() < 0.5) null else leftPtr

따라서 두 번의 호출로 같은 결과가 반환되지 않을 수 있습니다.



1

이 작업을 수행:

var left: Node? = null

fun show() {
     val left = left
     if (left != null) {
         queue.add(left) // safe cast succeeds
     }
}

허용되는 답변이 제공하는 첫 번째 옵션 인 것처럼 보이지만 이것이 당신이 찾고있는 것입니다.


이것은 "왼쪽"변수의 그림자입니까?
AFD


1

속성의 스마트 캐스트가 있으려면 속성 의 데이터 형식은 속성이 수퍼 클래스의 형식이 아니라 액세스하려는 메서드 나 동작을 포함하는 클래스 여야합니다 .


예를 들어 Android에서

있다:

class MyVM : ViewModel() {
    fun onClick() {}
}

해결책:

From: private lateinit var viewModel: ViewModel
To: private lateinit var viewModel: MyVM

용법:

viewModel = ViewModelProvider(this)[MyVM::class.java]
viewModel.onClick {}

GL


1

가장 우아한 솔루션은 다음과 같아야합니다.

var left: Node? = null

fun show() {
    left?.also {
        queue.add( it )
    }
}

그런 다음 새롭고 불필요한 로컬 변수를 정의 할 필요가 없으며 새로운 어설 션 또는 캐스트 (DRY가 아님)가 없습니다. 다른 범위 기능도 작동 할 수 있으므로 즐겨 찾기를 선택하십시오.


0

널이 아닌 어설 션 연산자를 사용해보십시오 ...

queue.add(left!!) 

3
위험한. 같은 이유로 자동 전송이 작동하지 않습니다.
Jacob Zimmerman

3
left가 null 인 경우 앱 충돌이 발생할 수 있습니다.
Pritam Karmakar

0

내가 쓰는 방법 :

var left: Node? = null

fun show() {
     val left = left ?: return
     queue.add(left) // no error because we return if it is null
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.