scala中的case class是一种特殊的对象:由编译器(compiler)自动生成字段的getter和setter。如下面的例子:
1 case class City(name:String, province: String) 2 case class Address(street: String, zip: String, city: City) 3 case class Person(name: String, age: Int, phone: String, address: Address) 4 val Peter = Person("Peter Chan",20,"4806111",Address("13 baoan road","40001",City("ShenZhen","GuangDong"))) 5 //> Peter : Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 6 //| baoan road,40001,City(ShenZhen,GuangDong)))
7 val John = Person("John Woo",43,"3602011",Address("33 fada road","51001",City("GuangZhou","GuangDong"))) 8 //> John : Exercises.LensDemo.Person = Person(John Woo,43,3602011,Address(33 fa 9 //| da road,51001,City(GuangZhou,GuangDong)))
10 val Ming = Person("Fang Ming",23,"3682412",Address("6 jiefang da dao","51012",City("GuangZhou","GuangDong"))) 11 //> Ming : Exercises.LensDemo.Person = Person(Fang Ming,23,3682412,Address(6 ji 12 //| efang da dao,51012,City(GuangZhou,GuangDong)))
这里我们可以看到:Person是个多层次对象,包含多层嵌入属性对象(multi-layer embeded objects)。如果需要更改Person类型实例中的任何字段时,我们可以直接用行令方式(imperative style):
1 case class City(var name:String, province: String) 2 Peter.address.city.name = "DongGuan"
3 Peter //> res0: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba 4 //| oan road,40001,City(DongGuan,GuangDong)))
注意:我必须把case class 属性City的name字段属性变成var,而且这时peter已经转变了(mutated)。既然我们是在函数式编程中,强调的是纯函数代码,即使用不可变对象(immutable objects),那么函数式编程方式的字段操作又可以怎样呢?
1 al peterDG = peter.copy( 2 address = peter.address.copy( 3 city = peter.address.city.copy(name = "DongGuan"))) 4 //> peterDG : Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address( 5 //| 13 baoan road,40001,City(DongGuan,GuangDong)))
6 peter //> res0: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba 7 //| oan road,40001,City(ShenZhen,GuangDong)))
8 peterDG //> res1: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba 9 //| oan road,40001,City(DongGuan,GuangDong)))
我们可以使用case class的自带函数copy来实现字段操作。但是随着嵌入对象层次的增加,将会产生大量的重复代码。scalaz的Lens type class的主要功能之一就可以解决以上问题。我们先来看看scalaz Lens的用例:
1 //定义Lens实例
2 val nameL = Lens.lensu[Person, String]((p,n) => p.copy(name=n), _.name) 3 //> nameL : scalaz.Lens[Exercises.LensDemo.Person,String] = scalaz.LensFunction 4 //| s$$anon$5@5f375618
5 val ageL = Lens.lensu[Person, Int]((p,a) => p.copy(age=a), _.age) 6 //> ageL : scalaz.Lens[Exercises.LensDemo.Person,Int] = scalaz.LensFunctions$$a 7 //| non$5@1810399e
8 val addrL = Lens.lensu[Person,Address]((p,a) => p.copy(address=a), _.address) 9 //> addrL : scalaz.Lens[Exercises.LensDemo.Person,Exercises.LensDemo.Address] 10 //| = scalaz.LensFunctions$$anon$5@32d992b2
11 val zipL = Lens.lensu[Address,String]((a,z) => a.copy(zip=z), _.zip) 12 //> zipL : scalaz.Lens[Exercises.LensDemo.Address,String] = scalaz.LensFunctio 13 //| ns$$anon$5@215be6bb
14 val cityL = Lens.lensu[Address,City]((a,c) => a.copy(city=c), _.city) 15 //> cityL : scalaz.Lens[Exercises.Le