Scala之var和val的比较&lazy懒加载

1:内容是否可变:val修饰的是不可变的,var修饰是可变的

下面看一段代码,你猜是否有错误

object ValAndVar {
//val 修饰由于不可变性必须初始化
val LOVE:String = _
var SEX:String = _

def main(args: Array[String]): Unit = {

val name = "tunan"
var age = 18

//val 修饰由于不可变性不能重新赋值
name = "zhangsan"
age = 19
}
}

真实的结果:

val和var的可变性比较

  1. val是不可变的,所以修饰的变量必须初始化
  2. val是不可变的,所以修饰的变量不能重新赋值
  3. val是不可变的,所以是多线程安全的
  4. val是不可变的,不用担心会改变它修饰的对象的状态
  5. val是不可变的,增强了代码的可读性,不用担心它的内容发生变化
  6. var是可变的,可以增强代码的灵活性,和val互补

2:val修饰的变量在编译后类似于java中的中的变量被final修饰

  1. 先看源代码

    object ValAndVar {
    val LOVE:String = "篮球"
    var SEX:String = _

    def main(args: Array[String]): Unit = {
    val name:String = "tunan"
    var age:Int = 18
    }
    }
  2. 再看反编译后的代码(只保留了我们想要的部分)

    public final class ValAndVar$ {
    public static ValAndVar$ MODULE$;
    private final String LOVE;

    public void main(String[] args) {
    String name = "tunan";
    int age = 18;
    }
    }

    我们发现这段代码很诡异,scala中的类变量,在字节码层面转换成了 parivate final ,而main方法中的变量却没有添加final修饰,这是否证明编译器有问题?

    答案是否定的,对于val或者final都只是给编译器用的,编译器如果发现你给此变量重新赋值会抛出错误。同时字节码(bytecode)不具备表达一个局部变量是不可变(immutable)的能力。

    所以就有了现在结果。

3:lazy修饰符可以修饰变量,但是这个变量必须是val修饰的

  1. 在证明lazy修饰的变量必须是val之前,我们先看看lazy是什么?

    Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。
    惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。

    在Java中,一般使用get和set实现延迟加载(懒加载),而在Scala中对延迟加载这一特性提供了语法级别的支持:

    lazy val name = initName()

    使用lazy关键字修饰变量后,只有在使用该变量时,才会调用其实例化方法。也就是说在定义name=initName()时并不会调用initName()方法,只有在后面的代码中使用变量name时才会调用initName()方法。

    如果不使用lazy关键字对变量修饰,那么变量name是立即实例化的,下面将通过一组案例对比认识:

    不使用lazy修饰的方法:

    object LazyDemo {
    def initName:String={
    println("初始化initName")
    return "返回intName"
    }
    def main(args: Array[String]): Unit = {
    // lazy val name = initName
    val name = initName //程序走到这里,就打印了initName的输出语句
    println("hello,欢迎来到图南之家")
    println(name) //程序走到这里,打印initName的返回值
    }
    }

    上面的name没有使用lazy关键字进行修饰,所以name是立即实例化的。

    结果:

    初始化initName
    hello,欢迎来到图南之家
    返回intName

    使用lazy修饰后的方法:

    object LazyDemo {
    def initName:String={
    println("初始化initName")
    return "返回intName"
    }
    def main(args: Array[String]): Unit = {
    lazy val name = initName //不调用initName方法,即不打印initName中的输出语句
    // val name = initName
    println("hello,欢迎来到图南之家") //打印main方法中的输出语句
    println(name) //打印initName的输出语句,打印返回值
    }
    }

    在声明name时,并没有立即调用实例化方法initName(),而是在使用name时,才会调用实例化方法,并且无论调用多少次,实例化方法只会执行一次。

    结果:

    hello,欢迎来到图南之家
    初始化initName
    返回intName
  2. 证明lazy只能修饰的变量只能使用val

    我们发现name都是使用val修饰的,如果我们使用var修饰会怎么样呢?

    lazy懒加载

    我们发现报错:'lazy' modifier allowed only with value definitions

    实际上就是认为lazy修饰的变量只能val修饰

Author: Tunan
Link: http://yerias.github.io/2020/03/19/scala/3/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.