概述:
case类在模式匹配和actor中经常使用到,当一个类被定义成为case类后,Scala会自动帮你创建一个伴生对象并帮你实现了一系列方法且带来了不少好处,如下:
1.实现了apply方法,意味着你不需要使用new关键字就能创建该类对象
1
2
3
4
5
|
scala>
case
class
People(name:String,age:Int)
defined
class
People
scala> val p = People(
"mobin"
,
22
)
//省略了new关键字
p: People = People(mobin,
22
)
|
2.实现了unapply方法,可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。
1
2
|
scala> p match {
case
People(x,y) => println(x,y) }
(mobin,
22
)
|
3.实现了类构造参数的getter方法(构造参数默认被声明为val),但是当你构造参数是声明为var类型的,它将帮你实现setter和getter方法(不建议将构造参数声明为var)
构造参数为val的情况(默认):
1
2
3
4
5
6
|
scala> p.name
res0: String = mobin
scala> p.name =
"mobin1"
//报错,因为构造参数被声明为val所以并没有帮你实现setter方法
<console>:
10
: error: reassignment to val
p.name =
"mobin1"
|
构造参数为var的情况:
1
2
3
4
5
6
7
8
9
10
11
|
scala>
case
class
People(var name:String)
//参数被声明为var
defined
class
People
scala> val p = People(
"mobin"
)
p: People = People(mobin)
scala> p.name =
"mobin2"
p.name: String = mobin2
scala> p.name
res1: String = mobin2
//修改成功,并没有报错
|
4.还默认帮你实现了toString,equals,copy和hashCode等方法
详述:
我们再通过反编译来看看当你定义一个case类时编译器是怎么做的:
同样定义一个简单的case类:
Person.scala
1
|
case
class
Person(name: String,age : Int)
|
通过终端中编译该文件(scalac Person.scala)后生成两个class文件,Person.class和Person$.class
接下来再通过javap Person命令反编译Person.class,结果结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Compiled from
"Person.scala"
public
class
com.mobin.scala.Person
implements
scala.Product,scala.Serializable {
public
static
scala.Function1<scala.Tuple2<java.lang.String, java.lang.Object>, com.mobin.scala.Person> tupled();
public
static
scala.Function1<java.lang.String, scala.Function1<java.lang.Object, com.mobin.scala.Person>> curried();
public
java.lang.String name();
public
int
age();
public
com.mobin.scala.Person copy(java.lang.String,
int
);
public
java.lang.String copy$
default
$
1
();
public
int
copy$
default
$
2
();
public
java.lang.String productPrefix();
public
int
productArity();
public
java.lang.Object productElement(
int
);
public
scala.collection.Iterator<java.lang.Object> productIterator();
public
boolean
canEqual(java.lang.Object);
public
int
hashCode();
public
java.lang.String toString();
public
boolean
equals(java.lang.Object);
public
com.mobin.scala.Person(java.lang.String,
int
);
}
|
再反编译Person$.class
1
2
3
4
5
6
7
8
9
|
Compiled from
"Person.scala"
public
final
class
com.mobin.scala.Person$
extends
scala.runtime.AbstractFunction2<java.lang.String, java.lang.Object, com.mobin.scala.Person>
implements
scala.Serializable {
public
static
final
com.mobin.scala.Person$ MODULE$;
public
static
{};
public
final
java.lang.String toString();
public
com.mobin.scala.Person apply(java.lang.String,
int
);
public
scala.Option<scala.Tuple2<java.lang.String, java.lang.Object>> unapply(com.mobin.scala.Person);
public
java.lang.Object apply(java.lang.Object, java.lang.Object);
}
|
通过反编译以上两个class文件可以知道,当你将一个类定义为case类后,编译器就自动帮你实现了一系列方法。
我们再来对比下去掉case关键字将类定义为普通类后反编译的结果
1
|
class
Person(var name : String,var age : Int)
//将参数声明为var
|
反编译的结果如下:
1
2
3
4
5
6
7
8
|
Compiled from
"Person.scala"
public
class
com.mobin.scala.Person {
public
java.lang.String name();
public
void
name_$eq(java.lang.String);
public
int
age();
public
void
age_$eq(
int
);
public
com.mobin.scala.Person(java.lang.String,
int
);
}
|
比较之下case类比普通的类多了不少的方法,所以当你不需要这些额外的方法时你就可以将类定义为普通的类,但是你又不想通过new关键字来创建实例,你可以在普通类中实现apply方法达到此目的。
因为case本就旨在创建的是不可变数据,所以在使用模式匹配时显得极为容易