Kotlin custom Getter сбивает с толку `val` и` var`? - PullRequest
0 голосов
/ 10 декабря 2018

В Kotlin var является изменяемым, и val должен назначаться только один раз.

Однако рассмотрим val foo в следующем примере:

var counter = 0

val foo: String
  get(){
    counter++
    return "val$counter"
  }

fun main(): String {
    val a = foo
    val b = foo
    val c = foo
    return "we got: $a $b $c"
    // output: we got: val1 val2 val3
}

get() метод выполняется каждый раз, когда мы пытаемся получить доступ к foo, в результате различные значения для val .

Поскольку значение foo меняется, я попытался использовать var.Затем компилятор пожаловался на «Свойство должно быть инициализировано».Поэтому мне пришлось задать ему значение по умолчанию:

var foo: String = "default value that will never be used"
  get(){
    counter++
    return "val$counter"
  }

Мне не нравится ни один из подходов.Какая правильная практика?

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

В Kotlin переменная является изменяемой, и значение val должно назначаться только один раз.

Для локальных переменных - да.Для свойств не совсем: val означает «есть только получатель», var означает «есть и получатель, и установщик».Этот геттер (и сеттер) может делать практически все что угодно.Вы можете просто возвращать случайное значение каждый раз, например.

Исключением является переназначение вспомогательного поля для val:

val foo: Int = 0
  get(){
    field++
    return field
  }

не будет компилироваться.

0 голосов
/ 10 декабря 2018

Об этом уже сообщается в YouTrack, как KT-16681 , "kotlin позволяет изменять поле свойства только для чтения".

Как вы можете видеть в ответе в KT-16681, пользовательский метод получения скомпилирован в другую функцию, в результате чего поле foo и метод getFoo() становятся двумя несвязанными вещами.

Также из ответа в KT-16681 это нарушение вида (Переназначение чтения-only свойство через поле поддержки) приведет к ошибке, так как Kotlin 1.3.

Обновление: В комментарии оригинальный автор упоминал, что KT-16681 отличается от этого вопроса.Однако, вдохновленный этой проблемой, мы можем видеть там байт-код Kotlin Tools -> Kotlin -> Show Kotlin Bytecode (удаленные метаданные и т. Д.):

public final class Test53699029Kt {


  // access flags 0xA
  private static I counter

  // access flags 0x19
  public final static getCounter()I
   L0
    LINENUMBER 3 L0
    GETSTATIC Test53699029Kt.counter : I
    IRETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  public final static setCounter(I)V
   L0
    LINENUMBER 3 L0
    ILOAD 0
    PUTSTATIC Test53699029Kt.counter : I
    RETURN
   L1
    LOCALVARIABLE <set-?> I L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x19
  public final static getFoo()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 7 L0
    GETSTATIC Test53699029Kt.counter : I
    DUP
    ISTORE 0
    ICONST_1
    IADD
    PUTSTATIC Test53699029Kt.counter : I
   L1
    LINENUMBER 8 L1
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "val"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    GETSTATIC Test53699029Kt.counter : I
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ARETURN
   L2
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x19
  public final static main()V
   L0
    LINENUMBER 12 L0
    INVOKESTATIC Test53699029Kt.getFoo ()Ljava/lang/String;
    ASTORE 0
   L1
    LINENUMBER 13 L1
    INVOKESTATIC Test53699029Kt.getFoo ()Ljava/lang/String;
    ASTORE 1
   L2
    LINENUMBER 14 L2
    INVOKESTATIC Test53699029Kt.getFoo ()Ljava/lang/String;
    ASTORE 2
   L3
    LINENUMBER 15 L3
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "we got: "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    BIPUSH 32
    INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    BIPUSH 32
    INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 3
   L4
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L5
   L6
    LINENUMBER 17 L6
    RETURN
   L7
    LOCALVARIABLE c Ljava/lang/String; L3 L7 2
    LOCALVARIABLE b Ljava/lang/String; L2 L7 1
    LOCALVARIABLE a Ljava/lang/String; L1 L7 0
    MAXSTACK = 2
    MAXLOCALS = 4

  // access flags 0x1009
  public static synthetic main([Ljava/lang/String;)V
    INVOKESTATIC Test53699029Kt.main ()V
    RETURN
    MAXSTACK = 0
    MAXLOCALS = 1

Как мы видим, для foo нет поля, просто getFoo(), сравнение для обычного объявления val:

public final class Test53699029Kt {


  // access flags 0xA
  private static I counter

  // access flags 0x19
  public final static getCounter()I
   L0
    LINENUMBER 1 L0
    GETSTATIC Test53699029Kt.counter : I
    IRETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  public final static setCounter(I)V
   L0
    LINENUMBER 1 L0
    ILOAD 0
    PUTSTATIC Test53699029Kt.counter : I
    RETURN
   L1
    LOCALVARIABLE <set-?> I L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1A
  private final static Ljava/lang/String; foo = "aaa"
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x19
  public final static getFoo()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 3 L0
    GETSTATIC Test53699029Kt.foo : Ljava/lang/String;
    ARETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  public final static main()V
   L0
    LINENUMBER 6 L0
    GETSTATIC Test53699029Kt.foo : Ljava/lang/String;
    ASTORE 0
   L1
    LINENUMBER 7 L1
    GETSTATIC Test53699029Kt.foo : Ljava/lang/String;
    ASTORE 1
   L2
    LINENUMBER 8 L2
    GETSTATIC Test53699029Kt.foo : Ljava/lang/String;
    ASTORE 2
   L3
    LINENUMBER 9 L3
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "we got: "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    BIPUSH 32
    INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    BIPUSH 32
    INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 3
   L4
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L5
   L6
    LINENUMBER 11 L6
    RETURN
   L7
    LOCALVARIABLE c Ljava/lang/String; L3 L7 2
    LOCALVARIABLE b Ljava/lang/String; L2 L7 1
    LOCALVARIABLE a Ljava/lang/String; L1 L7 0
    MAXSTACK = 2
    MAXLOCALS = 4

  // access flags 0x1009
  public static synthetic main([Ljava/lang/String;)V
    INVOKESTATIC Test53699029Kt.main ()V
    RETURN
    MAXSTACK = 0
    MAXLOCALS = 1

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 3 L0
    LDC "aaa"
    PUTSTATIC Test53699029Kt.foo : Ljava/lang/String;
    RETURN
    MAXSTACK = 1
    MAXLOCALS = 0

с использованием val foo = "aaa" даст нормальное поле final static String foo и метод final static String getFoo(), но использование val foo: String с get() не будетсоздайте это поле, просто создайте метод.Эта функция получения генерируется Kotlin, я полагаю, что потеря поля происходит из-за потери первоначального назначения в объявлении val, но я не могу найти реальную документацию по этому вопросу, такую ​​как Getters and Setters вKotlin просто прямо использует этот вывод.

Итак, кажется, что это обходной путь для модификации final static.

Проблемы возникают, когда неизменяемое поле ссылается на изменяемое поле.val не может быть переназначено, но оно ссылается на var, поле, которое можно переназначить, это приводит к изменению val.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...