scala中val与def定义的区别
变量
val
定义一个不可改变的变量, def
则是一个方法:
//scala中定义:
def main(args: Array[String]): Unit = {
def a = "hello"
val b = "hello"
println(a)
println(b)
}
//编译之后的class文件
private final String a$1()
{
return "hello";
}
public void main(String[] args)
{
String b = "hello";
Predef..MODULE$.println(a$1());
Predef..MODULE$.println(b);
}
方法
下面我们分别对应这四种定义方式,与反编译后的java比较一下,从而深层次理解def与val在方法中怎么实现的
class Test {
def even1: (Int => Boolean) = (_ % 2 == 0)
val even2: (Int => Boolean) = (_ % 2 == 0)
def even3(x: Int): Boolean = {
x % 2 == 0
}
lazy val even4: Int => Boolean = _ % 2 == 0
}
1. def a = ()
def even1: (Int => Boolean) = (_ % 2 == 0)
, 通过编译我们可以看到,每次调用even1 都会实例化一个对象,这显然不是我们愿意看到的
public Function1<Object, Object> even1()
{
new AbstractFunction1.mcZI.sp()
{
public static final long serialVersionUID = 0L;
public boolean apply$mcZI$sp(int x$1)
{
return x$1 % 2 == 0;
}
public final boolean apply(int x$1)
{
return apply$mcZI$sp(x$1);
}
};
}
2. val =
val even2: (Int => Boolean) = (_ % 2 == 0)
,实现了Function1, 并且实例化该对象
public Function1<Object, Object> even2()
{
return this.even2;
}
private final Function1<Object, Object> even2 = new AbstractFunction1.mcZI.sp()
{
public static final long serialVersionUID = 0L;
public boolean apply$mcZI$sp(int x$2)
{
return x$2 % 2 == 0;
}
public final boolean apply(int x$2)
{
return apply$mcZI$sp(x$2);
}
};
3.def a() = {}
这种方法定义相比java比较常规,容易接受
def even3(x: Int): Boolean = {
x % 2 == 0
}
编译之后如下:
public class Test
{
public boolean even3(int x)
{
return x % 2 == 0;
}
}
4.lazy val
lazy val even4: Int => Boolean = _ % 2 == 0
,对比even2, 实现了懒加载
private Function1<Object, Object> even4;
private volatile boolean bitmap$0;
public Function1<Object, Object> even4()
{
return this.bitmap$0 ? this.even4 : even4$lzycompute();
}
private Function1 even4$lzycompute()
{
synchronized (this)
{
if (!this.bitmap$0)
{
this.even4 = new AbstractFunction1.mcZI.sp()
{
public static final long serialVersionUID = 0L;
public boolean apply$mcZI$sp(int x$1)
{
return x$1 % 2 == 0;
}
public final boolean apply(int x$1)
{
return apply$mcZI$sp(x$1);
}
};this.bitmap$0 = true;
}
return this.even4;
}
}
我们再看一个比较复杂的例子
方法里面定义方法
class Test {
def fun1(x: Int): Int = {
def fun1_2() = {
println("fun1_2=>" + x)
}
fun1_2()
println("fun1")
2 * x
}
val fun2 = (x: Int) => {
val fun2_2 = () => {
println("fun2_2=>" + x)
}
fun2_2()
println("fun2")
2 * x
}
}
反编译后的java代码如下:
public class Test
{
private final void fun1_2$1(int x$1)
{
Predef..MODULE$.println(new StringBuilder().append("fun1_2=>").append(BoxesRunTime.boxToInteger(x$1)).toString());
}
public int fun1(int x)
{
fun1_2$1(x);
Predef..MODULE$.println("fun1");
return 2 * x;
}
public Function1<Object, Object> fun2()
{
return this.fun2;
}
private final Function1<Object, Object> fun2 = new AbstractFunction1.mcII.sp()
{
public static final long serialVersionUID = 0L;
public final int apply(int x)
{
return apply$mcII$sp(x);
}
public int apply$mcII$sp(final int x)
{
Function0 fun2_2 = new AbstractFunction0.mcV.sp()
{
public static final long serialVersionUID = 0L;
public final void apply()
{
apply$mcV$sp();
}
public void apply$mcV$sp()
{
Predef..MODULE$.println(new StringBuilder().append("fun2_2=>").append(BoxesRunTime.boxToInteger(x)).toString());
}
};
fun2_2.apply$mcV$sp();
Predef..MODULE$.println("fun2");
return 2 * x;
}
};
}
从例子可以看出来, def
方法比较简单唯一的区别是,把方法提出去形成并列的两个方法,把这种闭包形式通过形参的方式传递给下一个方法, 而val
方法通过实例化Function1接口来实现,对于fun2_2每次调用都会实例化对象,这个使我们编程中需要注意的