在Kotlin中,< code>var是可变的,而< code>val只能赋值一次。
但是,请考虑以下示例中的< code>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
}
每次尝试访问foo
,都会执行get()
方法,从而产生不同的val值。
由于< code>foo的值在变化,所以我尝试使用< code>var。然后编译器抱怨“属性必须初始化”。所以我必须给它一个默认值:
var foo: String = "default value that will never be used"
get(){
counter++
return "val$counter"
}
这两种方法我都不喜欢。正确的做法是什么?
这已经在YouTrack中报告为KT-16681,“kotlin允许修改只读属性字段”。
正如您在KT-16681中的回复中看到的那样,自定义getter被编译成另一个函数,这使得fieldfoo
和methodgetFoo()
成为两件不相关的事情。
同样从KT-16681中的回复来看,这种违反(通过支持字段重新分配只读属性)将产生一个错误,因为Kotlin 1.3。
更新:在评论中,原始海报提到KT-16681
与此问题不同。然而,受这个问题的启发,我们可以通过工具看到Kotlin字节码-
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
如我们所见,没有< code>foo
字段,只有一个< code>getFoo(),与普通的< code>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
字段和最终的静态 String getFoo()
方法,但使用 val foo: String
with get()
不会生成该字段,只需生成一个方法。这个 getter 函数是由 Kotlin 生成的,我相信字段的丢失来自 val
声明中初始赋值的丢失,但我找不到真正的文档,像 Kotlin 中的 Getters 和 Setters 这样的问题只是直接使用这个结论。
因此,这似乎是修改final static
的绕过。
当不可变字段引用可变字段时会出现问题val
不可重新分配,但它引用了一个可重新分配的字段<;code>var,这导致了<;code>val的修改。
在 Kotlin 中,var 是可变的,val 应该只分配一次。
对于局部变量,是的。对于属性来说,不是真的:< code>val表示“只有一个getter”,< code>var表示“同时有一个getter和一个setter”。这个getter(和setter)可以做几乎任何事情。例如,你可以每次返回一个随机值。
一个例外是重新分配 val
的支持字段:
val foo: Int = 0
get(){
field++
return field
}
不会编译。
Kotlin中的和有什么区别? 如本链接所述: 只读属性声明的完整语法与可变属性声明的不同之处在于两个方面:它以val而不是var开头,并且不允许setter。 但就在前面有一个使用setter的示例。 为什么我们两者都需要? 这不是Kotlin中变量的重复,与Java的区别:“var”vs.“val”?因为我询问的是与文档中的特定示例相关的疑问,而不仅仅是一般性的疑问。
在这种情况下我们必须使用val而不是var?我知道val是当我们知道值不会改变的时候。但我的印象是var对所有情况都是好的。是真的吗?换句话说:只用var有问题吗?
见 使用mixin组合映射层次 对于这个部分。
谷歌codelab Android Room with a View-静态编程语言有以下片段: 根据我对这个答案的理解,自定义getter每次都会被评估,而赋值只在构建时评估。因此,实际上,
我引用马丁·奥德斯基的话: def表单是“按名称”的,每次使用时都会对其右手边进行评估。 val定义的右侧是在定义本身处计算的。 null null 我有点困惑为什么:
3.4.4 使用混淆 自从 Gradle plugin for ProGuard 4.10 版本以后,Gradle 开始支持混淆。如果通过 Build Type 的 minifyEnabled 属性配置了使用混淆后,The ProGuard plugin 会自动被应用,并且自动创建一些任务。 android { buildTypes { release {