case class 概念
样例类,scala针对这种特殊类提供很多默认的方便的功能,更重要的是,和模式匹配完美配合
case class create
case class Fruit(name:String,weight:Double):
end Fruit
case class instance create
val fruit = Fruit("苹果",23.5)
case class背后的工作
scala会针对case class创建对应的伴生对象,并且自动重写了部分方法
- toString
- hashCode
- ==
- copy
- tupeld
val fruit = Fruit("苹果",23.5)
println(fruit)
结果
Fruit(苹果,23.5)
val fruit = Fruit("苹果",23.5)
val fruit2 = Fruit("苹果",23.5)
println(fruit == fruit2)
true
如果是一个普通的class
class Fruit2(name:String,weight:Double):
end Fruit2
val fruit = Fruit2("苹果",23.5)
val fruit2 = Fruit2("苹果",23.5)
println(fruit == fruit2)
结果
false
copy的便利性
val fruit = Fruit("苹果",23.5)
val fruit2 = fruit.copy(name = "橘子")
println(fruit2)
替换名称,其他数据不变,可以利用已有对象轻松创建新对象
tupled
利用元组转变为对应的case class
val tuple = ("苹果",23.5)
val fruit = (Fruit.apply _).tupled(tuple)
println(fruit)
关于tupled
Creates a tupled version of this function: instead of 2 arguments, it accepts a single Tuple2 argument.
Returns:
a function f such that f((x1, x2)) == f(Tuple2(x1, x2)) == apply(x1, x2)
hashcode
这个我们要看下生成的Java代码
public int hashCode() {
int var1 = -889275714;
var1 = Statics.mix(var1, this.productPrefix().hashCode());
var1 = Statics.mix(var1, Statics.anyHash(this.name()));
var1 = Statics.mix(var1, Statics.doubleHash(this.weight()));
return Statics.finalizeHash(var1, 2);
}
可以看出开头结尾有两次计算,中间经过和对应数据项的计算,也就是说有多少个字段,中间就有多少次哈希
这个Statics就是scala.Runtime下面的。
mix的实现
public static int mix(int hash, int data) {
int h = mixLast(hash, data);
h = Integer.rotateLeft(h, 13);
return h * 5 + 0xe6546b64;
}
有兴趣的话,可以逐一读一下源码
模式匹配的功能,这里暂时不展开。
编译观察
scalac Fruit.scala
使用javap查看
javap Fruit.class
结果
public class Fruit implements scala.Product,java.io.Serializable {
public static Fruit apply(java.lang.String, double);
public static Fruit fromProduct(scala.Product);
public static Fruit unapply(Fruit);
public Fruit(java.lang.String, double);
public scala.collection.Iterator productIterator();
public scala.collection.Iterator productElementNames();
public int hashCode();
public boolean equals(java.lang.Object);
public java.lang.String toString();
public boolean canEqual(java.lang.Object);
public int productArity();
public java.lang.String productPrefix();
public java.lang.Object productElement(int);
public java.lang.String productElementName(int);
public java.lang.String name();
public double weight();
public Fruit copy(java.lang.String, double);
public java.lang.String copy$default$1();
public double copy$default$2();
public java.lang.String _1();
public double _2();
}
javap Fruit$.class
结果
public final class Fruit$ implements scala.deriving.Mirror$Product,java.io.Serializable {
public static final Fruit$ MODULE$;
public static {};
public Fruit apply(java.lang.String, double);
public Fruit unapply(Fruit);
public java.lang.String toString();
public Fruit fromProduct(scala.Product);
public java.lang.Object fromProduct(scala.Product);
}
这样我们就不难理解它里面为什么有_1, _2这种方法,并且他还有apply和unapply,一个方便创建对象,一个可以进行模式匹配。