阅读本文后,您将学习到:
- 什么场景下使用var定义可变变量
- 什么场景下使用val定义不可变变量
- 什么场景下使用const val
可变变量var
使用场景
当我们定义的变量在后续的业务流程中,会随着一定的条件被重新赋值,这时需要把变变量定义为可变变量。
var name = "cola_wang"
if(满足条件) {
name = "var_cola_wang"
}
定义局部变量
java中定义局部变量:
public void main() {
string name = "cola_wang";
}
kotlin中定义局部变量
fun main() {
var name = "cola_wang"
}
定义成员变量
java中定义成员变量:
class Test {
public String name = "cola_wang";
}
kotlin中定义成员变量:
class Test {
var name = "cola_wangkai"
}
定义全局变量
java中定义全局变量:
class Test {
public static String name = "cola_wang"
}
kotlin中定义全局变量:
class Test {
companion object {
var name = "cola_wang"
}
}
显示声明和隐式声明
显示声明可变变量:在定义var变量时可以显示的声明变量的类型。
var name: String = "cola_wang"
隐式声明可变变量:在定义可变变量时可以不声明变量的类型,编译可以根据它的值推导出它的类型。
var name = "cola_wang" //编译器可以推导出name的类型为String类型
注意:如果定义的可变变量不直接赋值,那么这个可变变量需要显示的声明其类型,因为编译器无法推导出它的类型。
var name = "cola_wang" //定义的变量直接赋值了,编译器能够推导出其类型为String类型
var name //编译器无法推导出它的类型,报错,提示需要指定类型或赋初始值
name = ""
不可变变量val
使用场景
当我们定义的变量在后续的业务流程中,不会再给他重新赋值,我们需要把它定义为不可变变量。
val name = "cola_wang"
name = "xxxx" //编译器报错,不能修改
定义局部常量
java中定义局部常量:
public void main() {
final string name = "cola_wang";
}
kotlin中定义局部常量
fun main() {
val name = "cola_wang"
}
定义成员常量
java中定义成员常量:
class Test {
public final String name = "cola_wang";
}
kotlin中定义成员常量:
class Test {
val name = "cola_wangkai"
}
定义全局常量
java中定义全局常量:
class Test {
public static final String name = "cola_wang"
}
kotlin中定义全局常量,需要用const修饰:
class Test {
companion object {
const val name = "cola_wang"
}
}
显示声明和隐式声明
显示声明不可变变量:在定义变量时可以显示的声明变量的类型。
val name: String = "cola_wang"
隐式声明不可变变量:在定义不可变变量时可以不声明变量的类型,编译可以根据它的值推导出它的类型。
val name = "cola_wang" //编译器可以推导出name的类型为String类型
注意:如果定义的不可变变量不直接赋值,那么这个不可变变量需要显示的声明其类型,因为编译器无法推导出它的类型。
val name = "cola_wang" //定义的变量直接赋值了,编译器能够推导出其类型为String类型
val name //编译器无法推导出它的类型,报错,提示需要指定类型或赋初始值
name = "cola_wang"
思考
构造函数的参数用var/val修饰或不修饰有什么区别
这是一道我在面试迅雷时的面试题,现在记录一下。
先上代码:
class Test(val name: String, age: String) {
init {
println(name)
println(age)
}
fun main() {
println(name)
println(age) //编译报错,无法访问age
}
}
反编译查看kotlin文件编译后的代码,省略了一些非关键代码。从下面的代码可以看出:
- 被val/var修饰后的构造函数参数name,为类Test的成员变量,所以它在类中的任何位置可以被访问到。
- 没有被val/var修饰的构造函数参数age,只是方法的一个入参,它只能在构造函数内部或init代码块中被访问。
public final class Test {
@NotNull
private final String name;
public Test(@NotNull String name, @NotNull String age) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(age, "age");
super();
this.name = name;
String var3 = this.name;
System.out.println(var3);
System.out.println(age);
}
}
定义全局常量时,const val和val有什么区别
我们分别定义这两个类型的全局常量:
class Test {
companion object {
const val name = "cola_wang"
val age = "20"
}
}
查看反编译kotlin文件后的代码,总结一下:
- const val修饰的变量name被直接声明为Test的static final类型的常量,可以直接使用Test访问name,它在companion中并不存在。
- val修饰的变量age被声明为Test的static final类型的常量,它是私有的无法直接访问,在companion中创建了一个get方法来获取这个age变量,这个方法非静态的,所以访问age时,还实例化了Companion。
public final class Test {
@NotNull
public static final String name = "cola_wang";
@NotNull
private static final String age = "20";
@NotNull
public static final Companion Companion = new Companion((DefaultConstructorMarker)null);
public static final class Companion {
@NotNull
public final String getAge() {
return Test.age;
}
private Companion() {
}
}
}
除了以上区别,我们自定义一个User类,然后分别用val和const val修饰,看看下面的代码,所以const还有个特性是:它只能修饰基本数据类型和String类型。
class Test {
companion object {
const val user1 = User() //编译报错,提示const只能修饰基本数据类型和String类型
val user2 = User() //编译通过
}
}