case class浅析

结论

case class又称样例类,可用于DTO(序列化特性),模式匹配(unapply特性).
作为一种语法糖,在定义之后会隐式获得很多经典的方法.

1. case class与case object

无参数时,使用case object;
有参数时,使用case class.
case object比参数为()case classapplyunapply方法.

2. 类参数

类参数默认作为private final的类成员.
同时自动生成一个同名public方法以获取它的值.

3. 伴生对象

  • apply方法和unapply方法:
    case class会自动生成一个伴生对象(object),作为当前作用域的一个单例,同时在里面实现了apply方法,可以使用apply方法或者括号函数()创建对象.(也可以依旧使用new关键字)
    由于有unapply方法实现,因此可以用于模式匹配.

4. 序列化特性

详情

1. 用IDE查看class文件

假如写这么一个scala文件:

1
2
package learn
case class TestCaseClass()

编译后生成的class文件会有两个:

1
2
TestCaseClass$.class
TestCaseClass.class

用IDE打开TestCaseClass.class是:

1
2
3
package learn
case class TestCaseClass() extends scala.AnyRef with scala.Product with scala.Serializable {
}

可见定义case class后,自动会继承AnyRef,加上ProductSerializabletrait.
另一个内部类文件TestCaseClass$.class虽然用ide打开为空,但看文件大小1180,可见并不是空文件.

2. 反编译

使用javap -p -c -s TestCaseClass.class查看,过滤掉看不懂的部分和常量池里的各种符号引用,大致能发现如下内容:

  • 类签名

    1
    public class learn.TestCaseClass implements scala.Product,scala.Serializable

    可见本质上是实现Product和Serializable的接口.(加上默认实现)

  • 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static boolean unapply(learn.TestCaseClass);
    public static learn.TestCaseClass apply();
    public learn.TestCaseClass copy();
    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 learn.TestCaseClass();

    注意到unapplyapply方法是static的,说明会自动生成一个伴生对象,并添加这两个方法.(static方法在object里)

反编译内部类:

  • 类签名:
    1
    public final class learn.TestCaseClass$ extends scala.runtime.AbstractFunction0<learn.TestCaseClass> implements scala.Serializable
    可见case class其实也是作为一个Function存在的,如果把源码改动一下加上参数:
    1
    2
    package learn
    case class TestCaseClass(xyz:String)

则这里的类签名变为:

1
public final class learn.TestCaseClass$ extends scala.runtime.AbstractFunction1<java.lang.String, learn.TestCaseClass> implements scala.Serializable

此外由于类参数没有加限定,默认变成了val,因此类参数是private final的:

1
private final java.lang.String xyz

其中AbstractFuction是一个抽象类,源码为:

1
abstract class AbstractFunction0[@specialized(Specializable.Primitives) +R] extends Function0[R] {}
  • 方法签名:
    1
    2
    3
    4
    5
    public static {};// 3: invokespecial #15  // Method "<init>":()V
    public final java.lang.String toString();
    public learn.TestCaseClass apply();
    public boolean unapply(learn.TestCaseClass);
    public java.lang.Object apply();

推荐文章