目录
六、面向对象编程
c、方法的定义方式
在Scala语言中,方法参数前不能加val和var关键字来进行限定,所有的方法参数都是不可变类型,相当于隐式地使用了val关键字限定,如果在方法体里面给参数重新赋值,将不能通过编译。对于无参数地方法,定义时可以省略括号,不过需要注意,如果定义时省略括号,那么调用的时候也不能带有括号;如果无参数方法在定义时带有括号,则调用时可以带括号,也可以不带括号。在调用方法时,方法后面的圆括号()可以用{}代替。另外,如果方法只有一个参数,可以省略.号而采用中缀操作符调用方法,形式为“调用者 方法名 参数”。列如,现在对上面定义的Counter类进行改写 。具体代码如下:
class Counter {
var value = 0
def increment (step:Int):Unit = {value += step}
def current:Int = value
def getValue():Int = value
}
在这种定义下,对current方法的调用不能带括号,对getVlue的调用可带也可以不带括号。
d、封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它 部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java 封装操作如下,
- 将属性进行私有化
- 提供一个公共的 set 方法,用于对属性赋值
- 提供一个公共的 get 方法,用于获取属性的值
e、访问权限
在 Java 中,访问权限分为:public,private,protected 和默认。在 Scala 中,你可以通 过类似的修饰符达到同样的效果。但是使用上有区别。
- Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
- private 为私有权限,只在类的内部和伴生对象中可用。
- protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以 访问,同包无法访问。
- private[包名]增加包访问权限,包名下的其他类也可以使用
f、对象
Scala类中没有Java那样的静态成员。Scala采用单例对象来实现于JAVA静态成员同样的功能。单例对象的定义与类定义类似,只是用object关键字替换了class关键字。
- 单例对象采用 object 关键字声明
- 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
- 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
object Person {
private var lastId = 0
def newPersonId() = {
lastId +=1
lastId
}
}
有了这个单例对象,就可以直接通过其名字使用它,就像使用一个普通的类实例一样。
e、构造器
在Scala中,整个类定义主体就是类的构造器,称为主构造器,所有位于类方法以外的语句都将在构造过程中被执行。可以像定义方法参数一样,在类名之后用圆括号列出主构造器的参数列表。Scala类的构造器包括:主构造器和辅助构造器。
- 基本语法
class 类名(形参列表) { // 主构造器 // 类体 def this(形参列表) { // 辅助构造器 } def this(形参列表) { //辅助构造器可以有多个... } }
辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型 来区分;辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法;构造器调用其他另外的构造器,要求被调用构造器必须提前声明;
-
构造器参数
Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、var 修饰、val 修饰
- 未用任何修饰符修饰,这个参数就是一个局部变量
- var 修饰参数,作为类的成员属性使用,可以修改
- val 修饰参数,作为类只读属性使用,不能修改
g、继承和多态
- 基本语法
- val 修饰参数,作为类只读属性使用,不能修改
- 子类继承父类的属性和方法
- scala 是单继承
- 继承的调用顺序:父类构造器->子类构造器
- 动态绑定
- Scala 中属性和方法都是动态绑定,而 Java 中只有方法为动态绑定。
- 抽象类
- 基本语法
如果一个类包含没有实现的成员,则必须使用abtract关键字进行修饰,定义为抽象类。没有实现成员是指没有初始化的字段或者没有实现的方法。
abstract class Car{val name:String) {
val carSrand:String //字段没有初始化值,就是一个抽象字段
def cc() //抽象方法
def dd() {
println("Hello")
}
}
抽象类中的抽象字段必须要有声明类型。与Java不同的是,Scala里的抽象方法不需要加abstract修饰符。抽象类不能进行实例化,只能作为父类或子类被其他子类继承。
2. 继承&重写
像Java一样,Scala只支持单一继承,而不支持多重继承,即子类只能有一个父类。在类定义中用extends关键字表示继承关系。定义子类时,需要注意以下几个方面:
- 重写父类的抽象成员(包括字段和方法)时,override关键字是可选的;而重写父类非抽象的成员时,override关键字是必选的。建议在重写抽象成员是省略override关键字。这样做的好处是,如果随着业务的进展,父类的抽象成员被实现而成为非抽象成员时,子类相应成员由于没有override关键字,会出现编译错误,使用户能及时发现父类的改变,而如果子类成员原来就有override关键字,则不会有任何提醒。
- 只能重写val类型的字段,而不能重写var类型的字段。因为var类型本身就是可变的,所以,可以直接修改它的值,无需重写。
- 对于父类的主构造器中用var或val修饰的参数,由于其相当于类的一个字段,因此,如果子类的主构造器与父类的主构造器有相修饰名称的参数,则必须在子类的参数前加override修饰符,或者在子类的相同名称参数前去掉val或var,使其不自动成为子类的字段。
- 子类构造器必须调用父类的主构造器,所采用的方法是在extends关键字后的父类名称后跟上相应的参数列表,其中的参数个数和类型必须与父类的主构造器或者某个构造器一致。子类的辅助构造器不能调用父类的构造器。
h、apply 方法
- 说明
- 通过伴生对象的 apply 方法,实现不使用 new 方法创建对象。
- 如果想让主构造器变成私有的,可以在()之前加上 private。
- apply 方法可以重载。
- Scala 中 obj(arg)的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用 以统一面向对象编程和函数式编程的风格。
- 当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构 建对象时,调用的其实时伴生对象的 apply 方法。
object Test { def main(args: Array[String]): Unit = { //(1)通过伴生对象的 apply 方法,实现不使用 new 关键字创建对象。 val p1 = Person() println("p1.name=" + p1.name) val p2 = Person("bobo") println("p2.name=" + p2.name) } } //(2)如果想让主构造器变成私有的,可以在()之前加上 private class Person private(cName: String) { var name: String = cName } object Person { def apply(): Person = { println("apply 空参被调用") new Person("xx") } def apply(name: String): Person = { println("apply 有参被调用") new Person(name) } }
;
i、特质
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素, 所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with 关键字连接。
- 特质声明基本语法
trait 特质名 { trait 主体 }
;
-
特质基本语法
-
没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …
-
有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3…
-
-
说明
-
类和特质的关系:使用继承的关系。
-
当一个类去继承特质时,第一个连接词是 extends,后面是 with。
-
如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。
-
特质可以同时拥有抽象方法和具体方法
-
一个类可以混入(mixin)多个特质
-
所有的 Java 接口都可以当做 Scala 特质使用
-
动态混入:可灵活的扩展类的功能
-
动态混入:创建对象时混入 trait,而无需使类混入该 trait
-
如果混入的 trait 中有未实现的方法,则需要实现
trait PersonTrait { //(1)特质可以同时拥有抽象方法和具体方法 // 声明属性 var name: String = _ // 抽象属性 var age: Int // 声明方法 def eat(): Unit = { println("eat") } // 抽象方法 def say(): Unit } trait SexTrait { var sex: String } //(2)一个类可以实现/继承多个特质 //(3)所有的 Java 接口都可以当做 Scala 特质使用 class Teacher extends PersonTrait with java.io.Serializable { override def say(): Unit = { println("say") } override var age: Int = _ } object TestTrait { def main(args: Array[String]): Unit = { val teacher = new Teacher teacher.say() teacher.eat() //(4)动态混入:可灵活的扩展类的功能 val t2 = new Teacher with SexTrait { override var sex: String = "男" } //调用混入 trait 的属性 println(t2.sex) } }
特质和抽象类的区别:
-
优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
-
如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数, 而特质不行(有无参构造)。
-
-
-
j、扩展
- 说明
- obj.isInstanceOf[T]:判断 obj 是不是 T 类型。
- obj.asInstanceOf[T]:将 obj 强转成 T 类型。
- classOf 获取对象的类名。
- 枚举类和应用类
- 枚举类:需要继承 Enumeration
- 应用类:需要继承 App
object Test { def main(args: Array[String]): Unit = { println(Color.RED) } } // 枚举类 object Color extends Enumeration { val RED = Value(1, "red") val YELLOW = Value(2, "yellow") val BLUE = Value(3, "blue") } // 应用类 object Test20 extends App { println("xxxxxxxxxxx"); }
;
-
Type 定义新类型
使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
object Test {
def main(args: Array[String]): Unit = {
type S=String
var v:S="abc"
def test():S="xyz"
}
}