八、类
类是对象的模板, 属性(有什么)和方法(做什么)
类定义
package classes
// 简化写法
// class User
// 标准写法
class User {
}
object UserDemo {
def main(args: Array[String]): Unit = {
// 创建对象 "new" 关键字
val user1 = new User() // 调用类提供的默认无参构造
val user2 = new User
}
}
关键字
new
被用于创建类的实例,类默认提供一个无参的构造器
简单类
Scala中的类,类似于java,由属性和方法构成
package classes
class Student {
var name: String = "" // 必须初始化
var age: Int = 0
def sayHello(): String = {
"Hello:" + name + ",I am " + age + "years old"
}
// override def toString: String = this.name + "\t" + this.age
}
object StudentDemo {
def main(args: Array[String]): Unit = {
val s1 = new Student()
s1.name = "小红"
s1.age = 18
val s2 = new Student
s2.name = "小青"
s2.age = 20
println(s1) // 小红 18
println(s2) //小青 20
}
}
getter/setter
自动生成
class Person {
var name: String = null
val age : Int = 0
private var sex: Boolean = false
}
注意:
- 在scala类中,声明的属性都是私有成员
- 用
var
定义的属性,自动提供getter/setter方法- 用
val
定义的属性,自动提供getter方法- scala中的getter/setter,命名不同于java,语法是 name 和 name_
- 在scala中属性一旦设置为
private
, 它的getter/setter方法均为私有,仅限本类使用。
手动生成
getter方法 ----> def 区分成员名() = 成员值
setter方法 ----> def 区分成员名_(形参) {成员值 = 形参值}
package classes
class Dog {
private var privateColor: String = "black"
// 属性名和方法名不能相同
// def color(): String = privateColor
// 手动生成getter/setter方法
def color = privateColor
def color_(color: String): Unit = {
privateColor = color
}
}
object DogDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.color_("red")
println(dog.color)
}
}
@BeanProperty
import scala.beans.BeanProperty
class Fish {
// 自动生成scala和java风格的getter和setter
@BeanProperty var kind:String = ""
}
object Fish{
def main(args: Array[String]): Unit = {
val fish = new Fish
fish.kind = "金鱼"
println(fish.kind)
val fish2 = new Fish
fish2.setKind("鲫鱼")
println(fish2.getKind)
}
}
注意:scala的字段被标注为@BeanProperty时,会自动生成Java和Scala的getter/setter
反编译后的结果如下:
public class Person3 { private String name = null; public String name() { return this.name; } public void name_$eq(String x$1) { this.name = x$1; } public String getName() { return name(); } public void setName(String x$1) { name_$eq(x$1); } }
辅助构造器
类似于Java中重载的构造方法,Scala的类有一个主构造器(Primary Constructor)和任意多个辅助构造器(Auxiliary Constructor)
- 辅助构造器的名称为this
- 每一个辅助构造器必须以一个对先前已定义的其它辅助构造器或主构造器的调用开始
如:
package classes
class Fish {
var kind: String = ""
var name: String = ""
def this(name: String) {
this() // 调用主构造
this.name = name
}
def this(name: String, kind: String) {
this(name) // 调用前一个辅助构造器
this.kind = kind
}
}
object FishDemo{
def main(args: Array[String]): Unit = {
val f1 = new Fish("鲫鱼")
val f2 = new Fish("金鱼","xdd")
val f3 = new Fish()
}
}
主构造器
在scala中,每个类都有主构造器。主构造器并不以this方法定义,而是与类定义交织在一起
// 主构造器的参数写在类名之后 方法体为整个类
class Tiger(val color: String){
}
主构造的参数被编译成字段,其值被初始化成构造传入的参数。注意:主构造函数看上去和类的定义已经完全融合在了一起!它的参数列表放到了类名的后面(我们也可以直接叫它类参数列表),它的方法体就是整个类体,实例化一个类时,类体(主构造函数)中所有可行的部分都会被执行,不管是函数调用还是表达式等等,只是对于类的字段和方法声明而言是没有什么可执行的,它们只是声明而已。
// 主构造器的参数写在类名之后 color
class Tiger(val color: String) {
println("构造开始")
def sayHi(name: String): String = {
"Hello:" + name
}
println("构造结束")
}
object TigerDemo {
def main(args: Array[String]): Unit = {
val t1 = new Tiger("红色")
println("-------------------------------")
val t2 = new Tiger("白色")
}
}
/*
构造开始
构造结束
-------------------------------
构造开始
构造结束
*/
主构造的其它变化
// 对于var修饰的参数:外部可读/可改写 (实际上是:编译器为该类参数(字段)自动生成了getter和setter)
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 同上
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 对于val修饰的参数:外部可读/不可改写(实际上是:编译器为该类参数(字段)只生成了getter没有生成setter)
class Tiger3(var name: String, var age: Int, val sex: Boolean) {
}
// 对于private var修饰的参数:内部可读/可改写 (编译器不会为私有类参数(字段)自动生成getter和setter)
// 对于private val修饰的参数:内部可读/不可改写 (编译器不会为该类参数(字段)自动生成getter和setter)
class Tiger4(private var name: String) {
}
// 不带val或者var的参数,不会作为类的成员,只有被一个方法所使用,它将被升格为字段
class Tiger5(name: String, age: Int) {
def sayHi() = "Hello:" + name
}
八、对象
单例对象
Scala中没有静态方法或者静态属性,可以使用object
这样的语法达到相同目的。
package objects
object IdFactory {
private var id: Int = 0
def getId(): Int = {
id += 1
id
}
}
object TestIdFactory {
def main(args: Array[String]): Unit = {
println(IdFactory.getId()) // 1
println(IdFactory.getId()) // 2
println(IdFactory == IdFactory) // true
}
}
伴生对象
当类名和单例类名字一样的时候,我们把单例类称为伴生对象
package objects
// 伴生对象
object Person {
private var id: Int = 0
def getPersonNum(): Int = {
id += 1
id
}
def main(args: Array[String]): Unit = {
val p1 = new Person()
val p2 = new Person()
val p3 = Person
val p4 = Person
println(p1 == p2) // false
println(p3 == p4) // true
println(p1.id) // 1
println(p2.id) // 2
}
}
// 伴生类
class Person {
private var id: Int = Person.getPersonNum() // 伴生类中 可以调用伴生对象的私有方法
private var address: String = ""
}
注意:
- object声明的为伴生对象,class声明的为伴生类
- 伴生类可以访问伴生对象中的私有成员,前提是在同一源文件中,语法:伴生对象.私有成员|方法
apply & unapply
apply 方法用在object中一般作为工厂方法用于产生Class对象
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger( kind)
def main(args: Array[String]): Unit = {
val t1= Tiger.apply("东北虎")
val t2 = Tiger("东北虎")
}
}
apply方法的最佳实践方式之一就是用来做工厂。比如在Scala的标准库中,许多集合类给我们提供了apply方法来创建集合:
val a1 = Array(1,2,3) val b1 = List("Hello Hadoop","Hello Scala")
可以认为unapply方法是apply方法的反向操作,apply方法接受构造参数变成对象,而unapply方法接受一个对象,从中提取值
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger(kind)
def unapply(arg: Tiger): Option[String] = {
if (arg == null) None
else Some(arg.kind)
}
def main(args: Array[String]): Unit = {
val t1 = Tiger.apply("东北虎")
val t2 = Tiger("东北虎")
val Tiger(kind) = t2
println(kind) // 东北虎
}
}