Kotlin'de, null değerlerle başa çıkmanın, onları referans göstermenin veya dönüştürmenin deyimsel yolu nedir?


177

Null olabilecek bir türüm varsa Xyz?, ona başvurmak veya null olmayan bir türe dönüştürmek istiyorum Xyz. Kotlin'de bunu yapmanın deyimsel yolu nedir?

Örneğin, bu kod hatalıdır:

val something: Xyz? = createPossiblyNullXyz()
something.foo() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Xyz?"

Ama önce null değerini kontrol edersem buna izin verilir, neden?

val something: Xyz? = createPossiblyNullXyz()
if (something != null) {
    something.foo() 
}

Gerçekten hiç olmadığından emin olduğumu varsayarak, bir değeri kontrol nullgerektirmeden nasıl değiştirebilir veya tedavi edebilirim ? Örneğin, burada garanti edebileceğim bir haritadan bir değer alıyorum ve sonucu değil . Ama bir hata var:ifnullget()null

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")
something.toLong() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?"

Yöntem get(), öğenin eksik olabileceğini düşünüyor ve türü döndürüyor Int?. Bu nedenle, değerin türünü sıfırlanmaya zorlamanın en iyi yolu nedir?

Not: Bu soru yazar tarafından kasıtlı olarak yazılır ve cevaplanır ( Kendinden Cevaplanan Sorular ), böylece sık sorulan Kotlin konularına yönelik deyimsel cevaplar SO'da bulunur. Ayrıca Kotlin alfaları için yazılmış ve bugünkü Kotlin için doğru olmayan bazı eski cevapları açıklığa kavuşturmak.

Yanıtlar:


296

İlk olarak, Kotlin'deki vakaları iyice kapsayan Null Safety hakkında her şeyi okumalısınız .

KOTLIN olarak, bunu olmadığından emin olmadan bir null değeri erişemez null( koşullarda boş denetleniyor ) veya kesinlikle olmadığını iddia nullkullanarak !!emin operatörü , bir ile erişen ?.Güvenli Çağrısı son olarak muhtemelen bir şey veren ya nullbir ?:Elvis Operatörünü kullanarak varsayılan değer .

Sorunuzun içinde 1 durum için size bunlardan birini kullanmak istiyorsunuz kod niyet bağlı seçenekler vardır ve hepsi deyimsel ama farklı sonuçlar var:

val something: Xyz? = createPossiblyNullXyz()

// access it as non-null asserting that with a sure call
val result1 = something!!.foo()

// access it only if it is not null using safe operator, 
// returning null otherwise
val result2 = something?.foo()

// access it only if it is not null using safe operator, 
// otherwise a default value using the elvis operator
val result3 = something?.foo() ?: differentValue

// null check it with `if` expression and then use the value, 
// similar to result3 but for more complex cases harder to do in one expression
val result4 = if (something != null) {
                   something.foo() 
              } else { 
                   ...
                   differentValue 
              }

// null check it with `if` statement doing a different action
if (something != null) { 
    something.foo() 
} else { 
    someOtherAction() 
}

"Null işaretlendiğinde neden çalışıyor?" İçin, akıllı yayınlarda aşağıdaki arka plan bilgilerini okuyun .

Sorunuzun içinde 2 durum için birlikte söz konusu Mapgeliştirici olarak varlık asla sonucun eminiz eğer, nullkullanımı !!iddiası olarak emin operatörünü:

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")!!
something.toLong() // now valid

veya başka bir durumda, harita null OLABİLİR, ancak varsayılan bir değer sağlayabildiğinizde, Mapkendisinin bir getOrElseyöntemi vardır :

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.getOrElse("z") { 0 } // provide default value in lambda
something.toLong() // now valid

Arkaplan bilgisi:

Not: Aşağıdaki örneklerde, davranışı netleştirmek için açık türleri kullanıyorum. Tür çıkarımı ile, yerel değişkenler ve özel üyeler için normalde türler atlanabilir.

!!Emin operatör hakkında daha fazla bilgi

!!Operatör değeri olduğunu ileri sürer nullya da NPE atar. Bu, geliştiricinin değerin asla olmayacağını garanti ettiği durumlarda kullanılmalıdır null. Bunu bir iddia ve ardından akıllı bir oyuncu olarak düşünün .

val possibleXyz: Xyz? = ...
// assert it is not null, but if it is throw an exception:
val surelyXyz: Xyz = possibleXyz!! 
// same thing but access members after the assertion is made:
possibleXyz!!.foo()

devamını oku: !! Emin Operatör


nullKontrol ve Akıllı Yayınlar hakkında daha fazla bilgi

Null olabilecek bir türe bir nullçekle erişimi korursanız , derleyici akıllıca ifadenin gövdesindeki değeri null edilemez olarak kullanır. Bunun gerçekleşemeyeceği bazı karmaşık akışlar vardır, ancak yaygın durumlar için iyi çalışır.

val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
   // allowed to reference members:
   possiblyXyz.foo()
   // or also assign as non-nullable type:
   val surelyXyz: Xyz = possibleXyz
}

Veya isnull olmayan bir tür olup olmadığını kontrol ederseniz :

if (possibleXyz is Xyz) {
   // allowed to reference members:
   possiblyXyz.foo()
}

Aynı şey, aynı zamanda güvenli yayınlama için 'ne zaman' ifadeleri için de geçerlidir:

when (possibleXyz) {
    null -> doSomething()
    else -> possibleXyz.foo()
}

// or

when (possibleXyz) {
    is Xyz -> possibleXyz.foo()
    is Alpha -> possibleXyz.dominate()
    is Fish -> possibleXyz.swim() 
}

Bazı şeyler , değişkenin daha sonra kullanılması için nullçekin akıllı yayın yapmasına izin vermez . Kullanımlara Yukarıdaki örnek hiçbir şekilde uygulamanın akışında mutasyona olabileceğini yerel bir değişken, ister valveya varbu değişken bir dönüşmeye için hiçbir fırsatı buldu null. Ancak, derleyicinin akış analizini garanti edemediği diğer durumlarda, bu bir hata olacaktır:

var nullableInt: Int? = ...

public fun foo() {
    if (nullableInt != null) {
        // Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
        val nonNullableInt: Int = nullableInt
    }
}

Değişkenin yaşam döngüsü nullableInttam olarak görünmez ve diğer iş parçacıklarından atanabilir, nullkontrol boş bırakılamaz bir değere akıllı olarak dökülemez. Geçici bir çözüm için aşağıdaki "Güvenli Aramalar" konusuna bakın.

Akıllı oyuncu tarafından mutasyona uğramamaya güvenilemeyen başka bir durum, valözel alıcıya sahip bir nesnenin özelliğidir. Bu durumda, derleyicinin değeri değiştiren şeyin görünürlüğü yoktur ve bu nedenle bir hata mesajı alırsınız:

class MyThing {
    val possibleXyz: Xyz? 
        get() { ... }
}

// now when referencing this class...

val thing = MyThing()
if (thing.possibleXyz != null) {
   // error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
   thing.possiblyXyz.foo()
}

devamını oku: Koşullarda null olup olmadığını kontrol etme


?.Güvenli Arama operatörü hakkında daha fazla bilgi

Soldaki değer null ise güvenli çağrı operatörü null değerini döndürür, aksi takdirde sağdaki ifadeyi değerlendirmeye devam eder.

val possibleXyz: Xyz? = makeMeSomethingButMaybeNullable()
// "answer" will be null if any step of the chain is null
val answer = possibleXyz?.foo()?.goo()?.boo()

Bir listeyi yinelemek istediğiniz ancak nullboş olmasa da tekrarlamak istediğiniz başka bir örnek , yine güvenli arama operatörü işe yarar :

val things: List? = makeMeAListOrDont()
things?.forEach {
    // this loops only if not null (due to safe call) nor empty (0 items loop 0 times):
}

Yukarıdaki örneklerden birinde, bir ifçek yaptığımız ancak başka bir iş parçacığının değeri değiştirdiği ve dolayısıyla akıllı bir döküm yapma şansımız olduğu bir vakamız vardı . Bunu letçözme işleviyle birlikte güvenli çağrı operatörünü kullanmak için bu örneği değiştirebiliriz :

var possibleXyz: Xyz? = 1

public fun foo() {
    possibleXyz?.let { value ->
        // only called if not null, and the value is captured by the lambda
        val surelyXyz: Xyz = value
    }
}

devamını oku: Güvenli Aramalar


?:Elvis Operatörü hakkında daha fazla bilgi

Elvis operatörü, operatörün solunda bir ifade olduğunda alternatif bir değer sağlamanıza olanak tanır null:

val surelyXyz: Xyz = makeXyzOrNull() ?: DefaultXyz()

Bazı yaratıcı kullanımları da vardır, örneğin bir şey olduğunda bir istisna atın null:

val currentUser = session.user ?: throw Http401Error("Unauthorized")

veya bir işlevden erken dönmek için:

fun foo(key: String): Int {
   val startingCode: String = codes.findKey(key) ?: return 0
   // ...
   return endingValue
}

devamını oku: Elvis Operatörü


İlgili İşlevlere Sahip Boş İşleçler

Kotlin stdlib, yukarıda belirtilen operatörlerle gerçekten iyi çalışan bir dizi işleve sahiptir. Örneğin:

// use ?.let() to change a not null value, and ?: to provide a default
val something = possibleNull?.let { it.transform() } ?: defaultSomething

// use ?.apply() to operate further on a value that is not null
possibleNull?.apply {
    func1()
    func2()
}

// use .takeIf or .takeUnless to turn a value null if it meets a predicate
val something = name.takeIf { it.isNotBlank() } ?: defaultName

val something = name.takeUnless { it.isBlank() } ?: defaultName

İlgili konular

Kotlin'de, çoğu uygulama nulldeğerlerden kaçınmaya çalışır , ancak her zaman mümkün değildir. Bazen nullde mantıklı geliyor. Düşünülmesi gereken bazı yönergeler:

  • bazı durumlarda, yöntem çağrısının durumunu ve başarılı olursa sonucu içeren farklı dönüş türlerini garanti eder. Sonuç gibi kitaplıklar , kodunuzu da işleyebilecek bir başarı veya hata sonucu türü verir. Ve Kotlin için Kovenant adlı Promises kütüphanesi de aynı sözleri yerine getiriyor.

  • dönüş türleri olarak koleksiyonlar için null, üçüncü bir "mevcut değil" durumuna ihtiyacınız olmadığı sürece her zaman a yerine boş bir koleksiyon döndürür . Kotlin'in bu boş değerleri yaratmak emptyList()yaemptySet() da yaratmak için yardımcı işlevleri vardır .

  • varsayılan veya alternatifiniz olan bir boş değer döndüren yöntemler kullanırken, varsayılan bir değer sağlamak için Elvis işlecini kullanın. Bir Mapkullanım durumunda, nullable değeri döndüren yöntem getOrElse()yerine varsayılan bir değer oluşturulmasına izin verir . AynısıMapget()getOrPut()

  • Kotlin'in Java kodunun ?hükümsüzlüğünden emin olmadığı Java'dan yöntemleri geçersiz kılarken, imzanın ve işlevselliğin ne olması gerektiğinden eminseniz , nulllability'yi geçersiz kılma işleminizden çıkarabilirsiniz. Bu nedenle geçersiz kılınan yönteminiz daha nullgüvenlidir. Kotlin'de Java arabirimlerini uygulamak için de aynıdır, nullabiliteyi geçerli olduğunu bildiğiniz şekilde değiştirin.

  • Böyle gelince zaten yardımcı olabilir fonksiyonları, bakmak String?.isNullOrEmpty()ve String?.isNullOrBlank()hangi güvenle null değeri üzerinde işlem ve beklediğiniz yapabilirsiniz. Aslında, standart kütüphanedeki boşlukları doldurmak için kendi uzantılarınızı ekleyebilirsiniz.

  • gibi iddia fonksiyonları checkNotNull()ve requireNotNull()standart kütüphanede.

  • filterNotNull()null'ları koleksiyonlardan kaldıran veya listOfNotNull()olası bir nulldeğerden sıfır veya tek bir öğe listesi döndüren yardımcı işlevler .

  • Bir var Güvenli (null) dökme operatör mümkün değilse iyi ki NULL olmayan tip dönüş null bir döküm olanağı sağladığından yararlıdır. Ancak bunun için yukarıda belirtilen diğer yöntemlerle çözülmeyen geçerli bir kullanım durumum yok.


1

Önceki cevap takip edilmesi zor bir eylemdir, ancak işte hızlı ve kolay bir yol:

val something: Xyz = createPossiblyNullXyz() ?: throw RuntimeError("no it shouldn't be null")
something.foo() 

Eğer gerçekten hiç boş değilse, istisna olmaz, ama eğer olursa neyin yanlış gittiğini görürsünüz.


12
val bir şey: Xyz = createPossiblyNullXyz () !! createPossiblyNullXyz () null değerini döndürdüğünde bir NPE atar. Daha basittir ve boş olmadığını bildiğiniz bir değerle uğraşmak için sözleşmeleri izler
Steven Waterman
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.