第2章 Scala面向对象彻底精通
对象、类、特质(Trait)用来定义Scala的API和库,它们是Scala为面向对象编程提供的基础机制。Scala面向对象涉及的重要概念有class、object、构造器、字段、方法、抽象类、接口等,这些概念很多跟Java面向对象中的概念相似,但也有一些Scala独特之处。
2.1 Scala面向对象详解
2.1.1Scala中的class、object初介绍
在Scala中,类名不是必须和文件名相同且一个文件中可以定义多个class。
同时Scala的class中不存在静态成员,Java中的静态成员由Scala中的object对象替代,当object对象名和class名相同时则称object为class的伴生对象。
Scala类定义语法:
class ClassName{
}
关键字class是Scala中所有类定义的必须修饰符,包含普通类、抽象类、样例类等。
object定义的对象为单例对象。对于同名的object和class对象互称为伴生对象和伴生类,伴生对象和伴生类必须在同一个文件中。
class HiScala {
//一个私有的name属性和两个方法
private val name:String = "zhangsan"
def sayName(): Unit ={
println(name)
}
def getName = name
}
object HiScala{
def main(args: Array[String]): Unit = {
val hiScala = new HiScala
hiScala.sayName
val tmp = hiScala.getName
println(tmp)
}
}
object本身有默认的constructor,这个constructor只会被调用一次。
注意:
①伴生类和伴生对象一定要在同一个文件中;
②伴生对象可以访问伴生类的私有成员。
Scala 是单继承多立现的(用 with 关键字混人多个特质)
子类不能重写父类中被final修饰的方法和属性
abstract class Animal {
//子类不能重写父类中被final修饰的方法和属性
final val sex: String = "obj"
final def sayHi = {
println("Hi Scala")
}
def eat = {
println("Eat food!!!")
}
def run
}
class Cat extends Animal {
override def eat: Unit = {
println("Cat eating")
}
override def run: Unit = {
println("Cat Running")
}
}
class Dog extends Animal {
override def run: Unit = {
println("Dog is running")
}
}
object AnimalTest {
def main(args: Array[String]): Unit = {
val c = new Cat
c.eat
c.run
val d = new Dog
d.eat
d.run
}
}
2.1.2主构造器与辅助构造器
Scala默认构造器在类上,重载构造器,第一行必须是默认构造器或者其他构造器。
//默认无参构造器class Person{}
// 默认带一个或者多个参数的构造器
class Person(name: String) {
println(this.name)
//构造器重载 第一行必须是默认构造器
def this(name: String, age: Int) {
this(name)
println(name + " " + age)
}
def this(name: String, age: Int, sex: String) {
this(name, age)
println(name + " " + age)
}
}
object Person {
def main(args: Array[String]): Unit = {
//创建实例必须传入指定的参数
val s = new Person("name")
println(s)
}
}
2.1.3类的字段和方法彻底精通
var类型的属性Scala会帮助生成public的getter和setter方法;
val类型属性因为不可变,所以只有getter方法。
如果显示定义成private类型属性,Scala会生成私有的对应的getter和setter方法,外界无法访问。
Scala默认的方法是public的,
若在方法的前面加上private,则对象实例无法访问该方法。
若定义无参方法时不带括号,则调用时也不能写
如果字段或者方法被private[this]修饰,则该字段或方法只能被当前对象访问,当前对象的其他实例无法访问。
class Student {
/**
* var 属性会生成public的setter/getter方法
* val 属性只有getter方法
* private var属性会生成私有的setter/getter方法 外界无法访问
*
* 方法默认是public
* private方法 对象的实例无法访问该方法
*/
var name:String = "zhangsan"
var age = 30
val sex = "female"
private var myName = "Flink"
def sayHi = println("Hi!!!")
def increase(age:Int):Int=this.age+age
def myNames = this.myName
//复写setter方法
def myName_ (newName:String): Unit ={
myName = newName
println("Hi"+myName)
}
//自定义setter方法
def update(newName:String): Unit ={
myName = newName
println("Hi:"+myName)
}
//使用private [this]修饰 该字段或者方法只能被当前对象访问 当前对象的其他实例无法访问
private[this] val a = "Hello"
private[this] def test(): Unit ={
println("Testing")
}
}
object Student{
def main(args: Array[String]): Unit = {
val s = new Student
s.name
s.name = "lisi"
s.sex
}
}
2.1.4抽象类、接口的实战详解
将有abstract关键字的类称为抽象类。
抽象类中的字段不能显示用abstract
子类可以访问父类的非私有属性
抽象变量val和var定义,若一个类中定义了抽象成员(变量和方法),则该类必须定义为抽象类。
子类继承父类必须实现父类的抽象方法
abstract class P(name: String) {
println(this.name)
private var age = 20
//抽象类中的字段不能显示用abstract
//Student可以访问父类的非私有属性
val sex:String = "male"
}
class Student extends P("lilei"){
def main(args: Array[String]): Unit = {
val student = new Student
student.sex
}
}
abstract class AbstractTest{
val str:String
val strImpl:String="zhangsan"
val integer:Int
var bool:Boolean
def add(a:Int,b:Int):Int
def multi(x:Int,y:Int):Int={
x*y
}
}
/*继承抽象类,必须实现抽象类中未实现的成员*/
class AbstractTest1 extends AbstractTest{
override val str: String = "a"
override val strImpl: String = "b"
override val integer: Int = 1
override var bool: Boolean = false
override def add(a: Int, b: Int): Int = {
2+2
}
override def multi(x: Int, y: Int): Int = {
3+5
}
}
Scala的特质相当于Java中的接口,不过特质中可以有实现的方法。
在Scala中实现特质被称为混入,混入的第一个特质用关键字extends,
混入更多的特质用with关键字。
当子类混入这两个(或者多个)特质的相同字段时,需要重写该字段
trait PersonTrait {
val name:String
val age = 30
def eat(str:String)={
println(str)
}
}
trait WorkerTrait{
val age=25
def work: Unit ={
println("working")
}
}
class ClassStudent extends WorkerTrait with PersonTrait{
val name:String = "zhangsan"
//当子类混入这两个特质的相同字段时,需要重写该字段
override val age = 15
}
object Test{
def main(args: Array[String]): Unit = {
val s = new ClassStudent
println(s.eat("eating"))
println(s.work)
println(s.age)
}
}
2.1.5 Scala Option类详解
Scala的Option[T]是容器对于给定的类型的零个或一个元件。Option[T]可以是一些
[T]或None对象,它代表一个缺失值。
对比null值Java可用这表明没有任何值。
object OptionTest {
def main(args: Array[String]): Unit = {
val capital = Map("China"->"Peking","France"->"Paris")
println(capital.get("China"))
println(capital.get("India"))//当找不到时返回结果None
}
}
2.1.6 object的提取器
object中提供了apply方法。apply的作用是在创建对象的时候不直接用new,而是直接用对象加参数,这时调用了伴生对象的apply方法。
注意:对于抽象类也是可以直接调用伴生对象的apply方法。它的具体工作原理是先调用其伴生对象的apply方法,在这个方法中调用的是抽象类的子类的伴生对象的apply方法。
object ExtractorTest {
def main(args: Array[String]): Unit = {
println("Apply method" + apply("Zara", "gmail.com"))
println("UnApply method" + unapply("[email protected]"))
println("UnApply method" + unapply("Zara Ali"))
}
//注入
def apply(user: String, domain: String) = {
user + "@" + domain
}
//unapply 提取器
def unapply(str: String): Option[(String, String)] = {
val parts = str.split("@")
if (parts.length == 2) {
Some(parts(0), parts(1))
} else {
None
}
}
}
2.1.7Scala的样例类实战详解
样例类很简单也很重要,与后面的模式匹配结合使用。
样例类定义:
case class Person//类似于抽象类,只是关键字为case。
注意:样例类不能继承样例类。
abstract class Amount
//继承了普通类的2个样例类
case class Dollar(value:Double)extends Amount
case class Currency(value:Double,unit:String) extends Amount
//样例对象
case object Nothing extends Amount