代码之家  ›  专栏  ›  技术社区  ›  WSBT

Kotlin自定义getter使'val'和'var'混淆?

  •  3
  • WSBT  · 技术社区  · 7 年前

    在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的不同值 .

    因为 正在改变,我尝试使用 var . 编译器随后抱怨“必须初始化属性”。所以我必须给它一个默认值:

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

    我不喜欢这两种方法。正确的做法是什么?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Alexey Romanov    7 年前

    在Kotlin中,var是可变的,val只能分配一次。

    对于局部变量,是的。对于属性,不是: val 意思是“只有吸气剂”, var 意思是“有一个能手和一个二传手”。这个getter(和setter)几乎可以做任何事情。例如,您可以每次返回一个随机值。

    一个异常正在为 瓦尔 :

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

    不会编译。

        2
  •  2
  •   Geno Chen    7 年前

    这已经在您的跟踪中报告,作为 KT-16681 “Kotlin允许改变只读属性的字段”。

    正如您在KT-16681中的回复中看到的,自定义getter被编译成另一个函数,这使得字段 foo 方法 getFoo() 变成两件无关的事情。

    同样,从KT-16681中的回复中,这种违反(通过支持字段重新分配只读属性)将产生自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
    

    正如我们所看到的,这里没有 只是一个 获取GET FULL() ,比较正常 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() 不会产生那个字段,只是产生一个方法。这个getter函数是由kotlin生成的,我相信字段的丢失是由于在声明中丢失了初始赋值 瓦尔 但是我找不到真正的文件 Getters and Setters in Kotlin 直接使用这个结论。

    因此,这似乎是修改 final static .

    当不可变字段引用可变字段时,会出现问题。 瓦尔 不可重新分配,但它引用了 var ,一个可重新分配的字段,这导致修改 瓦尔 .