在Java
中如果想要定义一个变量,需要在变量名前面声明这个变量的类型,比如说int a
表示a
是一个整型变量,String b
表示b
是一个字符串变量。而Kotlin
中定义一个变量,只允许在变量前声明两种关键字——val
和 var
,类型通常在变量名的后面:
val a: Int = 10
val s: String = "I am Kotlin"
注意,Kotlin
每一行代码的结尾是不用加分号的。
为什么要采用这种风格呢?以下是Kotlin
官方FAQ
的回答:我们相信这样可以使得代码的可读性更好。同时,这也有利于使用一些良好的语法特性,比如省略类型声明(Scala
的经验表明,这不是一个错误的选择)。
关于“省略类型声明”的描述,是什么意思呢?为什么仅仅使用val
或者var
来声明一个变量,编译器就知道这个变量是什么类型呢?这也是Kotlin
比较有特色的一点,它拥有出色的类型推导机制,是Kotlin
在Java
基础上增强的语言特性之一。通俗地理解,编译器可以在不显式声明类型的情况下,自动推导出它所需要的类型:
val string = "I am Kotlin" // java.lang.String
val int = 1314 // int
val long = 1314L // long
val float = 13.14F // float
val double = 13.14 // double
val double2 = 10e6 // double
println(string.javaClass.name)
类型推导在很大程度上提高了Kotlin
这种静态类型语言的开发效率。
但是Kotlin
的类型推导机制并不总是可以正常工作的,如果对一个变量延迟赋值的话,Kotlin
就无法自动推导它的类型了。 这时候就需要显式地声明变量类型才行:
代码如下所示:
val s: String
显式地声明了变量s
为String
类型,此时Kotlin
就不会再尝试进行类型推导了。如果现在尝试将一个整形赋值给s
,那么编译器就会抛出类型不匹配的异常:
另外,Kotlin
中Int
的首字母是大写的,而Java
中int
的首字母是小写的。这表示Kotlin
完全抛弃了Java
中的基本数据类型,全部使用了对象数据类型。在Java
中int
是关键字,而在Kotlin
中Int
变成了一 个类,它拥有自己的方法和继承结构。
下图中列出了Java
中的每一个基本数据类型在Kotlin
中对应的对象数据类型:
val
和var
与Java
另一点不同在于,Kotlin
声明变量时,引入了val
和var
的概念:
var
(variable
):引用可变,这种变量在初始赋值之后仍然可以再被重新赋值;val
(variable + final
):引用不可变,这种变量在初始赋值之后就再也不能重新赋值,具有Java
中的final
关键字的效果;variable [ˈveriəblˌˈværiəbl] 可变的
用val
声明一个Int
类型的变量a
,尝试对变量a
修改:
val a = 10
a = a * 10
编译器会提示一个错误:
然后用val
声明一个指向数组的变量,然后尝试对其进行修改:
val x = intArrayOf(1, 2, 3)
x = intArrayOf(2, 3, 4)
编译器提示错误:
如果,对其中的某个元素进行修改:
val x = intArrayOf(1, 2, 3)
x[0] = 4
println("x[0] = ${x[0]}") // x[0] = 4
因为引用不可变,所以x
不能指向另一个数组,但可以修改x
指向数组的值。
val
声明的变量是只读变量,它的引用不可更改,但并不代表其引用对象也不可变。 如果把数组换成一个Book
类的对象,如下编写方式会变得更加直观:
class Book(var name: String) {
fun printName() {
println(this.name)
}
}
fun main() {
val book = Book("Thinking in Java")
book.name = "Diving into Kotlin"
book.printName() // Diving into Kotlin
}
val
关键字用来声明一个引用不可变的变量,而var
关键字用来声明一个引用可变的变量如下所示:
var a = 10
a = a * 10
println("a = $a") // a = 100
var x = intArrayOf(1, 2, 3)
x = intArrayOf(2, 3, 4)
既然val
关键字有这么多的束缚,为什么还要用这个关键字呢?全部用var
关键字不就好了。其实Kotlin
之所以这样设计,是为了解决Java
中final
关键字没有被合理使用的问题。
在Java
中,除非主动在变量前声明了final
关键字,否则这个变量就是可变的。然而这并不是一件好事,当项目变得越来越复杂,参与开发的人越来越多时,不知道什么时候一个可变的变量就被修改了,即使它原本不应该被修改,这就经常会导致出现一些很难排查的问题。因此,一个好的编程习惯是,除非一个变量明确允许被修改,否则都应该给它加上final
关键字。
不难发现副作用的产生往往与可变数据及共享状态有关, 有时候它会使得结果变得难以预测。比如,我们在采用多线程处理高并发的场景,“并发访问”就是一个明显的例子。
因此,Kotlin
在设计的时候就采用了和Java
完全不同的方式,提供了val
和var
这两个关键字,必须由开发者主动声明该变量是可变的还是不可变的。
在很多Kotlin
的学习资料中,都会传递一个原则:优先使用val
来声明变量。