'Değişken' için akıllı yayın yapmak imkansızdır, çünkü 'değişken' bu zamana kadar değiştirilebilen değiştirilebilir bir özelliktir


275

Ve Kotlin acemi, "aşağıdaki kod neden derlenmeyecek?" Diye soruyor:

    var left: Node? = null

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

'Düğüm' için akıllı yayın yapmak imkansızdır, çünkü 'sol' bu zamana kadar değiştirilebilen değiştirilebilir bir özelliktir

leftDeğişken bir değişken olsun , ama açıkça kontrol ediyorum left != nullve lefttürü Nodebu yüzden neden bu tür akıllı döküm olamaz?

Bunu zarif bir şekilde nasıl düzeltebilirim? :)


3
Farklı bir iş parçacığı arasındaki bir yerde değeri tekrar null olarak değiştirmiş olabilir. Eminim diğer soruların cevapları da bundan bahsediyor.
nhaarman


teşekkürler @nhaarman mantıklı, Whymarrh bunu nasıl yapabilirim? Güvenli aramaların sadece yöntemler değil nesneler için olduğunu düşündüm
FRR

6
Gibi bir şey: n.left?.let { queue.add(it) }Sanırım?
Jorn Vernee

Yanıtlar:


358

left != nullVe queue.add(left)başka bir iş parçacığının yürütülmesi arasında değeri değişmiş olabilirleft için null.

Bu sorunu çözmek için birkaç seçeneğiniz vardır. İşte bazıları:

  1. Akıllı yayın ile yerel bir değişken kullanın:

    val node = left
    if (node != null) {
        queue.add(node)
    }
  2. Aşağıdakilerden biri gibi güvenli bir çağrı kullanın :

    left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
  3. Kullanım Elvis operatörü ile returnkapatan işlevinden erken dönmek için:

    queue.add(left ?: return)

    Not breakve continuedöngüler içinde kontroller için benzer şekilde kullanılabilir.


8
4. Sorununuz için değiştirilebilir değişkenler gerektirmeyen daha işlevsel bir çözüm düşünün.
İyi Geceler Nerd Pride

1
@sak Sorunun Nodeorijinal sürümünde tanımlanan n.left, basitçe yerine daha karmaşık bir kod parçacığına sahip olan bir sınıf örneğiydi left. Cevabı buna göre güncelledim. Teşekkürler.
mfulton26

1
@sak Aynı kavramlar geçerlidir. valHer biri için yeni bir tane oluşturabilir var, birkaç ?.letifade iç içe yerleştirebilir veya ?: returnişlevinize bağlı olarak birkaç ifade kullanabilirsiniz . örn MyAsyncTask().execute(a1 ?: return, a2 ?: return, a3 ?: return). "Çok değişkenli izin" için çözümlerden birini de deneyebilirsiniz .
mfulton26

1
@FARID kimlerden bahsediyor?
mfulton26

3
Evet, güvenli. Bir değişken sınıf global olarak bildirildiğinde, herhangi bir evre değerini değiştirebilir. Ancak bir yerel değişken (bir işlev içinde bildirilen bir değişken) durumunda, bu değişken diğer iş parçacıklarından erişilemez, bu nedenle kullanımı güvenlidir.
Farid

31

1) Ayrıca kullanabilirsiniz lateinitEğer varsa emin sonradan üzerinde başlatma yapmak onCreate()veya başka bir yerde.

Bunu kullan

lateinit var left: Node

Bunun yerine

var left: Node? = null

2)!! Değişken sonunu bu şekilde kullandığınızda başka bir yol daha vardır

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

bu ne işe yarıyor?
c-an

@ c-an değişkeninizin null olarak başlatılmasını sağlar ancak daha sonra kodda başlatmayı bekler.
Radesh

Öyleyse, aynı değil mi? @Radesh
c-an

@ c-a ne ile aynı?
Radesh

1
'Sol' bu kez değişmiş olabilir değişebilir bir özellik olduğundan, bu kod değişken tür belirterek bu hatayı önler çünkü yukarıdaki soru 'Düğüm' Akıllı döküm mümkün değildir. derleyici akıllı oyuncuya gerek yok
Radesh

27

Mfulton26'nın cevabına ek olarak dördüncü bir seçenek var.

?.İşleci kullanarak, yöntemleri ve alanları ele almadan uğraşmak mümkündürlet ya da yerel değişkenleri kullanarak.

Bağlam için bazı kodlar:

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

Yöntemler, alanlar ve işe yaramaya çalıştığım diğer tüm şeylerle çalışır.

Bu nedenle, sorunu çözmek için manuel dökümler kullanmak veya yerel değişkenler kullanmak ?.yerine yöntemleri çağırmak için kullanabilirsiniz .

Referans için, bu Kotlin'de test edildi 1.1.4-3 , aynı zamanda test 1.1.51ve 1.1.60. Diğer sürümlerde çalışacağının garantisi yok, yeni bir özellik olabilir.

Kullanmak ?. sorun olarak bir geçti değişken olduğundan operatörü sizin durumunuzda kullanılamaz. Elvis operatörü alternatif olarak kullanılabilir ve muhtemelen en az kod gerektiren bir operatöre sahiptir. continueYine returnde kullanmak yerine, kullanılabilir.

Manuel döküm kullanmak da bir seçenek olabilir, ancak bu null güvenli değildir:

queue.add(left as Node);

Farklı bir iş parçacığında sol değiştiğinde , program çökecektir.


Anladığım kadarıyla, '?' operatör sol tarafındaki değişkenin boş olup olmadığını kontrol ediyor. Yukarıdaki örnekte 'kuyruk' olacaktır. 'Akıllı döküm imkansız' hata "add" yöntemine geçirilen "sol" parametresi atıfta ... Bu yaklaşımı kullanırsanız hala hata alıyorum
FRR

Doğru, hata açık leftve değil queue. Bunu kontrol etmek gerekiyor, cevabı bir dakika içinde düzenleyecek
Zoe

4

Bunun işe yaramamasının pratik nedeni, ipliklerle ilgili değildir. Mesele şu ki node.left, etkin bir şekilde tercüme edilmiştir node.getLeft().

Bu mülk alıcı şu şekilde tanımlanabilir:

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

Bu nedenle, iki çağrı aynı sonucu vermeyebilir.


2

Değişim var left: Node? = nulliçin lateinit var left: Node. Sorun çözüldü.



1

Özelliklerin Akıllı bir Dökümü olması için, özelliğin veri türü, erişmek istediğiniz yöntemi veya davranışı içeren sınıf olmalıdır ve özellik süper sınıf türünde DEĞİLDİR.


örneğin Android'de

Be:

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

Çözüm:

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

Kullanımı:

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

GL


1

En şık çözümünüz:

var left: Node? = null

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

O zaman yeni ve gereksiz bir yerel değişken tanımlamanız gerekmez ve yeni iddialarınız veya yayınlarınız (DRY olmayan) yoktur. Diğer kapsam işlevleri de işe yarayabilir, bu yüzden favorinizi seçin.


0

Boş olmayan onaylama işlecini kullanmayı deneyin ...

queue.add(left!!) 

3
Tehlikeli. Aynı nedenle otomatik döküm çalışmaz.
Jacob Zimmerman

3
Sol boşsa uygulama kilitlenmesine neden olabilir.
Pritam Karmakar

0

Nasıl yazarım:

var left: Node? = null

fun show() {
     val left = left ?: return
     queue.add(left) // no error because we return if it is null
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.