"const"和"val"有什么区别?

459

我最近了解了const关键字,但是我很困惑!我找不到constval关键字之间的任何区别,我的意思是我们可以使用两者来创建一个不可变的变量,我是否还有遗漏的内容?


3
我会尽力为您翻译以下内容,保持原意并使之更加通俗易懂。请注意,下面是需要翻译的内容:https://kotlinlang.org/docs/reference/properties.html#compile-time-constantsKotlin支持编译时常量,这是在编译时就已知的值。要将属性声明为编译时常量,需要使用const修饰符进行标记,并将其分配给基本类型或String类型的值。这些属性可以用于注释、默认参数或者在一些其他方面中直接使用。由于编译时常量是在编译时确定的,因此它们具有许多限制:它们不能有自定义getter、它们不能引用类的非常量成员、它们不能在类初始化期间使用等等。 - Michael
1
您可以阅读此文章 https://www.android4dev.com/difference-between-var-val-and-const-in-kotlin/ 或观看此视频 https://www.youtube.com/watch?v=DQLrEGqSSI8&t=6s - Lokesh Desai
9个回答

619

const是编译时常量。这意味着它们的值必须在编译时分配,而不像val那样可以在运行时分配。

这意味着只能将字符串或基本类型赋值给const,而不能赋值给函数或类构造函数的结果。

例如:

const val foo = complexFunctionCall()   //Not okay
val fooVal = complexFunctionCall()      //Okay

const val bar = "Hello world"           //Also okay

4
像这样的东西:const val foo = "Hello world"val bar = "Hello world"怎么样?它们是一样的吗? - Mathew Hany
5
@MathewHany,至少就字节码而言不是这样的,请参考:https://dev59.com/zFoU5IYBdhLWcg3wamca#37485356 - hotkey
5
我认为const变量在编译期间会被完全内联。 - Luka Jacobowitz
175
这引出了另一个问题:为什么 Kotlin 要求使用 const val 而不仅仅是 const?在我看来,这种情况下 val 关键字完全是多余的,因为 const var 本身就是荒谬的。 - Eric Lloyd
47
使用 const val 时,const 是对 val 进行修饰的一个关键字,而不是一个独立的关键字。在 Kotlin 中,修饰符比关键字更为重要。类似的设计还有:注解/枚举/数据类私有变量内联函数等。 - Aro
显示剩余9条评论

57

补充一下Luka的答案:

编译时常量

在编译时已知值的属性可以使用const修饰符标记为编译时常量。这些属性需要满足以下要求:

  • 顶层或对象声明伴生对象的成员。
  • 用String类型或基本类型的值进行初始化
  • 没有自定义getter

这些属性可以在注释中使用。

来源:官方文档


39
你可以将Kotlin转换为Java。 然后你会发现constval多了一个static修饰符。 代码如下。

Kotlin:


然后你会看到 constval 多了一个 static 修饰符。 代码如下。
const val str = "hello"
class SimplePerson(val name: String, var age: Int)

转 Java(部分):

@NotNull
public static final String str = "hello";

public final class SimplePerson {
   @NotNull
   private final String name;
   private int age;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public SimplePerson(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
      this.age = age;
   }
}

3
有人能否在评论中说明为什么这个答案被 downvote 到无人问津? - James Jordan Taylor
3
@JamesJordanTaylor 我点了赞。但我认为这是因为有些人没有仔细阅读,快速浏览下来,这个答案似乎在谈论如何从Java转换到Kotlin,这将与主题无关。 - WSBT
2
如果删除const,会产生不同的Java文件吗? - DYS
3
@DYS:我认为它会移除“static”,并且只是公共的final字符串str =“hello”; - Varun Ajay Gupta
SimplePerson类的private final String name;相比,@DYS的属性没有使用关键字const,并且也是私有的。但这是因为它是作为一个成员变量而不是作为顶级/包变量存在,并不是因为const关键字的原因。 - nobled

30

将 Kotlin 转换为 Java

const val Car_1 = "BUGATTI" // final static String Car_1 = "BUGATTI";

将 Kotlin 转换为 Java

val Car_1 = "BUGATTI"   // final String Car_1 = "BUGATTI";

用简单的语言解释

  1. 常量变量的值在编译时已知。
  2. val的值用于在运行时定义常量。

示例1-

const val Car_1 = "BUGATTI"val Car_2 = getCar() ✔    
const val Car_3 = getCar() ❌ 

//Because the function will not get executed at the compile time so it will through error

fun getCar(): String {
    return "BUGATTI"
}
这是因为getCar()在运行时被评估并将值分配给Car。
此外 -
1. val是只读的,意味着它在运行时是不可变的。 2. var是可变的,意味着它在运行时是可变的。 3. const是在编译时已知的不可变变量。

23

valconst 都是不可变的。

const 用于声明编译时常量,而 val 用于运行时常量。

const val VENDOR_NAME = "Kifayat Pashteen"  // Assignment done at compile-time

val PICon = getIP()  // Assignment done at run-time

编译时在运行时之前发生,对吧? - whatwhatwhat
2
@whatwhatwhat 是的,代码在被发送执行之前会被编译。代码执行的时间点就是运行时执行的本质。 - Arpan Sircar
1
@whatwhatwhat 是的,编译时发生在运行时之前。 - androminor
val 不一定是不可变的。 - Tenfour04

11

因为我读了很多书,所以认为“val”表示不可变:这绝对不是这种情况,只需看这个例子:

class Test {
    var x: Int = 2
    val y
        get() = x
}

fun main(args: Array<String>) {
    val test = Test()
    println("test.y = ${test.y}") // prints 2
    test.x = 4
    println("test.y = ${test.y}") // prints 4
}

可悲的是,目前你只能通过const来实现真正的不可变性-但这仅在编译时有效。在运行时,你无法创建真正的不可变性。

val只是指“只读”,你不能直接更改此变量,只能间接地更改,就像我在上面的示例中所示。


哇,非常棒! - Sheldon Wei
1
即使在您的示例中,它也是不可变的。您将y定义为一个返回x中任何内容的函数。这个函数不能被重新分配给另一个函数。 - Marko Bjelac
你只是通过重写的getter方法返回另一个变量的值!你没有改变Y,即没有重新分配它! - Mohammed Junaid

6

对于那些想要知道在valconst之间哪个更合适或更有效的人:

对于字符串或任何原始数据类型,建议使用const val而不是val。因为val将在运行时确定,所以当您的应用程序正在运行时,它将处理所有值。另一方面,const val将在编译时更早地进行处理。因此从性能角度来看,const val会产生更好的结果。


6

val

Kotlin 的 val 关键字是与 Kotlin var 关键字相对应的一种只读属性。另一种称之为只读属性的名称是 immutable

Kotlin 代码:

val variation: Long = 100L

Java的等价代码如下:

final Long variation = 100L;

常量值

我们可以使用const关键字来定义不可变属性。对于在编译时已知的属性,我们会使用const。这是二者之间的区别。请注意,const属性必须在全局范围内声明。

Kotlin代码(在 playground 中):

const val WEBSITE_NAME: String = "Google"

fun main() {    
    println(WEBSITE_NAME)
}

Java代码(在playground中):

class Playground {

    final static String WEBSITE_NAME = "Google";

    public static void main(String[ ] args) {
        System.out.println(WEBSITE_NAME);
    }
}

只读并不等同于不可变,因此该答案的第二个句子是错误的。你可以拥有一个只读的 val,通过自定义 getter 或者作为委托属性,在多次调用时产生不同的结果,或者因为它是开放的并且在子类中具有 setter。 - Tenfour04
Kotlin中的val关键字用于只读属性,那么为什么你在示例中要对它进行写入操作呢? - Marian Paździoch
@Tenfour04 read-only === 不可变的,可以看看我的评论:https://dev59.com/pFoU5IYBdhLWcg3wTVvE#PTPunYgBc1ULPQZFgT_k - Marko Bjelac
@Tenfour04 只读 === 不可变的,请查看我的评论:https://stackoverflow.com/questions/37595936/what-is-the-difference-between-const-and-val#comment132669180_70486344 - undefined
@MarkoBjelec 你所说的有反例: https://pl.kotl.in/DZ-c6drq0 - Tenfour04

5

我们通过一个例子来学习这个。

object Constants {

    val NAME = "Amit"

}

注意:我们并没有使用const关键字。
而我们是通过以下方式访问NAME的:
fun testValWithoutConst() {
    val name = Constants.NAME
}

现在,我们需要反编译这段代码。为此,我们需要将这个 Kotlin 源文件转换为 Java 源文件。

我们将得到以下输出:

public final void testValWithoutConst() {
  String name = Constants.INSTANCE.getNAME();
}

输出结果符合预期。

上面的例子没有使用const关键字。现在,让我们使用const关键字。

为此,我们将修改Kotlin中的对象类Constants如下:

object Constants {

    const val NAME = "Amit"

}

注意:我们正在使用const
而且,我们可以像下面这样访问NAME
fun testValWithConst() {
    val name = Constants.NAME
}

现在,当我们反编译这段代码时,我们将得到以下输出:
public final void testValWithConst() {
  String name = "Amit";
}

在这里,我们可以看到变量NAME已被其值Amit替换。
由于该值已内联,因此运行时访问该变量将没有开销。因此,它将导致应用程序的更好性能。
这是在Kotlin中使用const的优点。
参考我的博客:在Kotlin中使用const的优点

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接