toString
有时候我们需要以字符串的形式提交信息,例如在调试时输出到控制台。那么,如何将非文本对象表示为可读字符串呢?这就需要用到 toString()
函数。
引言
假设我们有三个盒子,分别装有不同种类的浆果:覆盆子(raspberry)、草莓(strawberry) 和 蓝莓(blueberry)。我们需要这些盒子的重量信息,并打印出来:
val raspberryWeight = 10
val strawberryWeight = 15
val blueberryWeight = 20println(raspberryWeight) // 10
println(strawberryWeight) // 15
println(blueberryWeight) // 20
解释代码:
这看起来没问题。我们定义了三个变量,存储每种浆果的重量,并通过 println() 输出了它们。
现在我们定义一个 BerryHolder 类,用来存储这些盒子的重量,并再次尝试打印这些值:
class BerryHolder(val weight: Int)val raspberryWeight = BerryHolder(10)
val strawberryWeight = BerryHolder(15)
val blueberryWeight = BerryHolder(20)println(raspberryWeight) // BerryHolder@6f496d9f
println(strawberryWeight) // BerryHolder@723279cf
println(blueberryWeight) // BerryHolder@10f87f48
解释代码:
这次的结果看起来就不太理想了,输出的并不是我们期望看到的重量,而是类名和内存地址。
为什么会这样?
要理解这一点,我们需要知道 println(message: Any?)
是怎么工作的。它的参数类型是 Any?
,这意味着 Kotlin 中的所有类(标准类或自定义类)都可以传递进去。
由于 println()
要处理任意类型的对象并将其输出为字符串,所以它会隐式地调用对象的 toString()
函数。
默认行为
toString()
函数定义在 Any
类中,这是所有类的父类。因此,任何类都继承了 toString()
方法。
但默认的 toString()
实现,返回的是类名 + 内存地址的字符串。
对于一些内置类型,比如 Int
或 Double
,toString()
方法已被重写以提供更有意义的输出:
val nonString = 1.0println(nonString.toString()) // 1.0
println(nonString) // 1.0
解释代码:
上面两种写法效果相同,println()
隐式调用了 Double
类型的 toString()
。
但对于大多数类,如果没有特别重写 toString()
,仍然会输出类名+内存地址。
重写 toString()
为了让我们的类输出有意义的字符串内容,我们需要重写 toString()
方法。比如:
class BerryHolder(val weight: Int) {override fun toString(): String {return weight.toString()}
}
println(BerryHolder(10)) // 10
解释代码:
现在,我们创建的 BerryHolder
对象在被打印时会输出它的重量,而不是内存地址。
更复杂的例子
现在我们要开发一个电子图书馆系统,其中有一个 User
类,包含用户的 ID、登录名和邮箱。
我们希望输出格式类似于:
User{id=id_value, login=login_value, email=email_value}
class User(val id: Int, val login: String, val email: String) {override fun toString(): String {return "User{id=$id, login=$login, email=$email}"}
}val user = User(1, "uncle_bob", "rmartin@objectmentor.com")
println(user) // User{id=1, login=uncle_bob, email=rmartin@objectmentor.com}
解释代码:
通过重写 toString()
,我们成功将 User
对象转换为清晰的字符串形式,输出包含所有关键信息。
重写 toString():继承情况
当子类继承父类时,如果父类已经重写了 toString()
,子类也会继承这个行为。
比如,我们添加一个 Author
类继承 User
,并添加图书信息:
open class User(val id: Int, val login: String, val email: String) {override fun toString(): String {return "User{id=$id, login=$login, email=$email}"}
}class Author(id: Int, login: String, email: String, val books: String): User(id, login, email)val user = User(1, "marys01", "mary0101@gmail.com")
val author = Author(2, "srafael", "rsabatini@gmail.com", "Captain Blood: His Odyssey")println(user) // User{id=1, login=marys01, email=mary0101@gmail.com}
println(author) // User{id=2, login=srafael, email=rsabatini@gmail.com}
解释代码:
由于 Author
没有重写 toString()
,所以调用的是 User
中的重写版本。
为子类单独重写 toString()
现在我们在 Author
中也重写 toString()
,并输出书籍信息:
class Author(id: Int, login: String, email: String, val books: String): User(id, login, email) {override fun toString(): String {return "Author{id=$id, login=$login, email=$email}, books: $books"}
}
解释代码:
这样我们就能看到更多信息了:
val user = User(1, "marys01", "mary0101@gmail.com")
val author = Author(2, "ohwilde", "wilde1854@mail.ie", "Someone’s portrait")println(user) // User{id=1, login=marys01, email=mary0101@gmail.com}
println(author) // Author{id=2, login=ohwilde, email=wilde1854@mail.ie}, books: Someone’s portrait
在子类中调用父类的 toString()
有时候我们希望在子类的 toString()
方法中复用父类的逻辑,这时候可以使用 super
:
class Author(id: Int, login: String, email: String, val books: String): User(id, login, email) {override fun toString(): String {return "Author: ${super.toString()};\nBooks: $books"}
}
测试一下:
val author1 = Author(1, "uncle_bob","rmartin@objectmentor.com","\n1.\"Clean Code: A Handbook of Agile Software Craftsmanship\" \n2.\"Agile Software Development: Principles, Patterns and Practices\"")val author2 = Author(2, "ltlst","leotolstoy@mail.com","\n1.\"Anna Karenina\" \n2.\"The Death of Ivan Ilyich\" \n3.\"War and Peace\"")println(author1)
println()
println(author2)
输出结果为:
Author: User{id=1, login=uncle_bob, email=rmartin@objectmentor.com};
Books:
1."Clean Code: A Handbook of Agile Software Craftsmanship"
2."Agile Software Development: Principles, Patterns and Practices"Author: User{id=2, login=ltlst, email=leotolstoy@mail.com};
Books:
1."Anna Karenina"
2."The Death of Ivan Ilyich"
3."War and Peace"
总结
-
toString()
函数可以将非字符串对象转换为字符串。 -
Kotlin 中的所有类都继承了
Any
,因此默认拥有toString()
方法。 -
默认情况下,
toString()
返回的是类名和内存地址。 -
你可以通过重写
toString()
自定义对象的打印方式。 -
对于继承结构,子类可以重用或扩展父类的
toString()
方法。
现在你已经掌握了如何使用和重写 toString()
方法,能够更好地进行调试和信息展示了!