最近在数据服务项目中,整个项目是spring mvc架构,但是考虑到更多的是与数据打交道,而scala的函数式编程与map,filter等这些高级函数对于数据操作来说,相比笨重的Java就显得通俗很多,整个代码量也会少很多。于是在maven环境下配置了scala环境,并在dao层与service层使用了scala code。其中碰到了一些坑。其中scala class问题纠缠我许久,所以觉得有必要记录一下。
scala class
和Java一样,用class关键字定义的一个类。
class User
val user1 = new User
User默认带有一个无参的构造函数。
class Point(var x: Int, var y: Int) {
def move(dx: Int, dy: Int): Unit = {
x = x + dx
y = y + dy
}
}
val point1 = new Point(2, 3)
Point类有三个成员,x,y变量和move函数。构造函数在class定义(var x: Int, var y: Int)上,接受两个Int参数。我们都知道,在Java中如果定义了有参的构造函数,如果没有显式定义无参构造函数的话,便不会有无参构造函数。同样如果定义Point类时,添加了x,y属性,这时候Point便会有个两个参数的构造函数,相应的无参构造函数便需要显式定义了。如果没有定义,便没有无参构造函数。
在spring jdbcTemplate对sql查询返回映射成class时,这时候需要对class做反射,在实例对象的时候,使用的是 Class.newInstance() 方法,在newInstance方法,如果当期class没有无参构造方法,变化抛出InstantiationException异常。
* @throws IllegalAccessException if the class or its nullary
* constructor is not accessible.
* @throws InstantiationException
* if this {@code Class} represents an abstract class,
* an interface, an array class, a primitive type, or void;
* or if the class has no nullary constructor;
* or if the instantiation fails for some other reason.
这是newInstance()方法会抛出异常的情况,“if the class has no nullary constructor“ 说明了如果用newInstance方法实例化一个对象时,class必须有无参构造函数。而我们在使用scala定义class时,如果在定义函数头时指明了属性,那么默认情况下便没有无参构造函数了。如果需要添加无参构造函数的话,定义this()函数。
class Point(var x: Int, var y: Int) {
def this(){
this(0,0)
}
}
通过调用this(Int,Int)来实现定义this()无参构造函数。但如果定义Point时未在class头定义属性,便只有一个无参构造函数,如果还想定义有参数构造函数,即想通过this()来实现this(Int,Int),暂时我还未找到方法。
在官网class tour的Constructors章节中,属性值设置默认值,在实例化对象时可以少加参数或者不加参数的情况,只是scala默认值的语法糖,如果用Java代码实例化这个对象的话,还是只有两个参数的构造函数,并不会有无参构造函数和一个参数的构造函数。
class Point(var x: Int = 0, var y: Int = 0)
val origin = new Point // x and y are both set to 0
val point1 = new Point(1)
println(point1.x) // prints 1
var、val、private、 @BeanProperty
参数前如果用var修饰的话,scala将生产setter和getter方法,但是并不是setX这样的函数名,如果想要setX这样的函数名,使用@BeanProperty对属性进行修饰;val修饰的话,只有getter方法,因为val修饰的属性为不可变的。另外如果在属性前添加private关键字的话,便阻止了setter和getter方法。
PS: fastjson gson jaskjson对scala对象序列化问题
因为在spring中@ResponseBody会将对象转换成json返回给前端,而使用何种json转换器可以通过xml中配置。在其他遇到了以下问题:
- fastjson对某些对象转换为空,原因是fastjson在对对象进行序列化的时候好像需要class有getter,setter方法(这个可以具体研究下序列化过程,gson不需要class有getter/setter方法也可以进行序列化),所以在scala class属性中加上@BeanProperty注解即可。
- 因为项目中使用了swagger文档化,swagger要求json按照jackson序列化形式进行序列化,所以原则上对Jackson最支持,后续由于有人提issue,加入了fastjson支持,但仍未对gson进行支持,在issue1608中有人提到,应该在计划当中了。
最终因为项目架构是同事搭建的,为了少改他的结构,在最终Controller转换选择了fastjson转换,所以scala class每个属性都需要添加@BeanProperty注解。
case class
使用了case关键字的类定义就是case classes。使用这个关键字,Scala编译器会自动为你定义的类生成一些成员。
首先,编译器为case class生成一个同名的对象构造器(object construction),也就是你可以使用 Var(“x”) 来创建一个类的实例,而无需使用new Var(“x”),它会调用object的apply方法。
case class Var(name:String)
val x = Var("x")
其他用法与class类似。