Чем интеллектуальное приведение отличается от явного приведения в KOTLIN - PullRequest
2 голосов
/ 02 августа 2020

Недавно я прочитал об умном приведении, выполняемом оператором is, а также об операторах as или лучше as?, которые используются для явного приведения.

Документы kotlin виды их различий в использовании следующим образом: -

Обратите внимание, что интеллектуальное приведение не работает, если компилятор не может гарантировать, что переменная не может измениться между проверкой и использованием. Более конкретно, интеллектуальное приведение применяются в соответствии со следующими правилами:

  • локальные переменные val - всегда, кроме локальных делегированных свойств;

  • свойства val - если свойство закрытый или внутренний, либо проверка выполняется в том же модуле, где объявлено свойство. Интеллектуальное приведение не применимо к открытым свойствам или свойствам, у которых есть настраиваемые геттеры;

  • var локальные переменные - , если переменная не изменяется между проверкой и использованием, не фиксируется в лямбде, который его изменяет, и не является локальным делегированным свойством;

  • свойства var - никогда (, потому что переменная может быть изменена в любое время другими code ).

Обратите внимание, что интеллектуальное приведение не работает, если компилятор не может гарантировать, что переменная не может измениться между проверкой и использованием.

Сказанное выше немного сбивает с толку, поскольку переменные var могут быть изменены после инициализации, и я не смог найти пример, который бы прояснил реальное понимание оператора.

Может ли кто-нибудь в любом случае упростить понимание этой идеи?

И дает ли оператор is некоторое преимущество оптимизации по сравнению с as оператор если есть?

1 Ответ

3 голосов
/ 02 августа 2020

Идея умного приведения состоит в том, чтобы помочь вам избежать использования as или as? для явного приведения того, что уже было проверено. Что касается пунктов, указанных выше, вот несколько примеров.

  • val локальные переменные - поскольку val является окончательным (не может быть изменено), после выполнения проверки переменная может быть умно преобразована как он не может измениться снова.
val a: Int? = 2
if (a is Int) {
    // 'a' is smart cast to Int
    val b = a * 2 // b is also Int
}
  • свойства val - при прямом доступе (через геттер по умолчанию) возможно умное приведение. Если через пользовательский метод получения, это не потому, что мы не можем знать, что он был изменен.
class Test {
    val a: Int? = 2;
}

class TestGetter {
    val a: Int? = 2
        get() = field * 2
}

// Usage
val test = Test()
val testGetter = TestGetter()

if (test.a is Int) {
    // 'test.a' is smart cast to Int
    val b = test.a * 2
}

if (testGetter.a is Int) {
    // smart cast is impossible, 'testGetter.a' is still Int?
    val b = testGetter.a * 2 // can't happen because we don't know whether 'a' was changed by the custom getter or not (the getter could give unstable values)
}
  • var локальные переменные - если переменная не изменилась между проверкой и использованием, не захвачено в лямбде, которая его изменяет, и не является локальным делегированным свойством;
var a: Int? = 2
if (a is Int) {
    // 'a' was not changed, so it can be smart cast to Int
    val b = a * 2 // b is also Int
}

var c = 4
if (c is Int) {
    c = null
    // 'c' was changed between the check and the usage, we cannot smart cast it anymore
    val b = c * 2 // won't work
}

свойства var - var всегда может быть изменен чем-то другим в коде, поэтому умное приведение не будет работать.

class Example {
    var a: Int? = 2

    fun test1() {
        if (a is Int) {
            // smart cast is impossible because we don't know whether 'a' was changed by some other code/function
            val b = a * 2 // won't work
        }
    }
}

Что касается использования as, если вы посмотрите на последний пример:

class Example {
    var a: Int? = 2

    fun test1() {
        if (a is Int) {
            // smart cast is impossible because we don't know whether 'a' was changed by some other code/function
            val b = a as Int * 2 // this WILL work because we forcefully cast it to Int, but if a is null there will be an exception in runtime
        }
    }
}

Вы также можете использовать as?, когда не уверены может ли var быть приведено к чему-либо или нет. Если нет, он просто даст вам ноль. Например:

val a: Double = 2.0
val b = a as? String // 'b' will be 'String?', in this case initialized to 'null' since 'a' cannot be cast to it

val c: Int? = 2
val d = c as? Int // 'd' will be '2' but still 'Int?' since 'as?' always makes the variable nullable

Надеюсь, примеры помогли, дайте мне знать, если мне нужно что-то уточнить.

...