变量 var str: String 是可变的还是不可变的?

14

我在 Kotlin 中声明了一个字符串变量,如下所示。

var str: String

Kotlin 文档对于可变性的概念存在矛盾。 根据文档... var 是可变的。 var is mutable

但是对于 String,它定义为不可变的。 String is immutable

因此,请澄清这个矛盾...

6个回答

11
实际上,String变量是可变的,但String值是不可变的。
感谢@cricket_007的支持。
让我更深入地描述当您声明变量时发生了什么。
val string1 = "abcd"  
val string2 = "abcd"

enter image description here

如上图所示并声明:

-字符串池是堆内存中的特殊存储区域。

-当创建一个字符串时,如果该字符串已经存在于池中,则返回现有字符串的引用,而不是创建新对象并返回其引用。

-如果字符串不是不可变的,则使用一个引用更改字符串会导致其他引用的值错误。

-现在我们将上面的例子值分配给变量String1,现在我们可以使用这个变量。

同时,我们也可以更改该值。

string1.renameTo("Hi This Is Test")

在内存中发生了什么?如果可用字符串“Hi This Is Test”,它将返回对“string1”的引用,否则它会创建新空间并给出对“string1”的引用。这就是为什么字符串被称为不可变的原因。参考链接:Link

8
我并不喜欢文档的写法,但实际上并没有矛盾。他们使用单词“mutable”来比较程序符号和对象,这并不是很有帮助。
用关键字“var”声明的变量是可重新分配的,而用关键字“val”声明的变量则不可重新分配。
字符串是一个不可变的对象类型,一旦创建就无法更改。
一个变量是否可重新分配以及它所指向的对象是否不可变是两个不同的概念。
以下是一个例子:
class Person(var name: String)

val fred = Person("Fred")

fred.name = "Barry" // fred is not immutable

fred = Person("Barry") // ERROR Val cannot be reassigned

因此,按照文档的方式使用mutable,仅仅因为一个val声明的symbol并不意味着它所指向的object是不可变的。


是的,但可变和可重新分配是一样的吗? - RBK
同意它们都是可重新分配的。但这并不意味着可变 = 可重新分配。这就是我想澄清的。 - RBK
@FrankWilson 很棒的例子! - kirtan403
@RBK 不,可变和可重新赋值不是同一个概念。可变指的是变量所指向的值 - 数据。可重新赋值则是指你是否可以将变量指向不同的数据(不同的值)。 - Scooter
1
此外,类定义后面的大括号可以被移除。 - Salem
@1blustone,我已经实现了你的建议。谢谢。 - Frank Wilson

5

由@Frank提供的优秀示例让我更清楚地理解了文档所说的内容。

文档的第一部分说明:

Kotlin 中的类可以具有属性。这些属性可以使用 var 关键字声明为可变的,也可以使用 val 关键字声明为只读的。

现在,第二部分说明:

字符串由 String 类型表示。字符串是不可变的。

在我看来,这两者都是正确的。

基于 Frank 的例子,我们再举一个例子。

data class User(var name: String, var email:String)

var user1 = User("Foo","foo@bar.com")  
// user1 is mutable and object properties as well

val user2 = User("Bar","bar@foo.com")
// user2 is immutable but object's properties are mutable

现在考虑属性user1。它是可变的,因为它是用关键字var声明的,并且赋值了User对象。
但是user2属性是不可变的。您不能更改分配给它的对象。但是对象本身具有可变属性。因此,可以通过user2.name = "Foo"更改属性。
现在稍微更改一下示例,并使用户属性变为不可变。
data class User(val name: String, val email:String)

var user1 = User("Foo","foo@bar.com")  
// user1 is mutable and object properties are not

val user2 = User("Bar","bar@foo.com")
// user2 is immutable and object's properties are also immutable

这里,用户的属性是不可变的。因此,user1 是可变的,您可以将另一个对象分配给它。但是属性是不可变的,所以 user1 = User("New Foo","newfoo@bar.com") 可以运行。但是在分配了 User 对象之后,您无法更改其属性,因为它们是不可变的。

现在,让我们来看一个字符串类型的例子。

var str1 = "Bar"
// Here str1 (property) is mutable. So you can assign a different string to it. 
// But you can not modify the String object directly.
str1 = "Foo"  // This will work
str1[0] = 'B' // This will NOT work as The string object assigned to str1 is immutable

val str2 = "Foo"
// Here both str2 and the assigned object are immutable. (Just like Java final)

正如Frank所说:

仅仅因为一个符号被声明为val,并不意味着它指向的对象是不可变的。

我的最终观点:

字符串对象是不可变的,因为它不能被改变。但是这个不可变的字符串对象可以被赋值给一个可变的属性,这个属性可以被再次赋值为不同的字符串对象。这就是var关键字所做的。将属性变为可变的。但是它所指向的对象可以是可变或不可变的。


3
什么是矛盾之处?字符串是只读的。就像Java一样,您不能设置 a [i] ='x',任何字符串替换方法都会返回新字符串等。因此,不可变。这一点是为了澄清 var String类型的功能。
var和val之间的区别可以与Java中的 final 变量相关联。您可以创建一个 final String 常量,或者您可以拥有一个普通的 String 对象。

虽然 val 和 var 与 Java 的 final 相关,但是使用 var 声明的字符串是可变的还是不可变的? - RBK
3
根据我所解释的和文件所说的,它总是不可变的……如果一个字符串是可变的,你究竟希望做什么? - OneCricketeer

1

就个人而言,我觉得更容易从Java的角度来思考valvar之间的区别。val相当于一个带有final修饰符的变量,这意味着它只能被赋值一次,而var则没有这个修饰符,因此可以重新赋值。

对象本身仍然可以是可变或不可变的。这里的可变性是指变量本身。


0
在 Kotlin/Java 中,对象的行为不取决于您用于访问它的引用类型。一切都(可能)在堆中,因此任何属性只是指向对象的引用(又称链接)。
val str = "Hello"
val a = str
var b = str

在上面的代码片段中,只有一个不可变的字符串str,而ab都引用它。无论你使用可变或不可变的引用,你都不能改变这个字符串。但是,你可以更改可变引用本身,使其指向另一个(不可变的)字符串:
b = "World"

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