scala-面向对象程序设计一

1.类的定义

class 列名(参数列表){    //构造器:列名(构造参数列表)
	成员变量(字段) var /val
    成员方法(方法) def,方法的参数都是val
    辅助构造器: //辅助构造器:this(构造参数列表),类似Java的构造方法重载,构造器都没有返回值
}
//*************************分割线*************************************
eg:
class Dog(val name: String,var age){  //自动生成类的属性 
  def cry()=println("wow ...") 
} 
val dog = new Dog("Harry",2) 
dog.name="Wang"  //编译错误 
dog.age=3    //编译通过 

主构造器中,使用 val的定义的变量自动生成 getter,var 定义的变量自动生
成 getter&setter。在 Scala 中,严格来说是没有 getter 和 setter 这个说法,使用了“value”和“value_=”两个方法来分别代替了 getter 和 setter。如下所示。 
class Counter(var value:Int) 
class Counter{ 
    private var privateValue = 0;//私有变量,外界无法直接访问 
    def value = privateValue;//定义一个方法,代替 getter 
    def value_= ( newValue : Int ){// value_= 是方法名字,代替 setter 
         value =  newValue; 
    } //注意,scala 中默认方法是 public 的 
} 

代码样列

class Student{
    //定义成员变量
    var name = "张三"
    var age = 19
    var gender:String =_ //null
    val banji = "1班"
    
    //定义一个辅助构造器
    def this(name:String,age:Int)={
        //辅助构造器必须从调用其他构造器开始
        this()
        this.name = name
        this.age = age
    }
    //定义一个辅助构造器2
    def this(gender:String){
        this("李四",12)
        this.gender = gender
    }
    //定义一个成员方法
    def study()={
        println("成员方法")
    }
}

object Test{
    main{   //def main(args: Array[String]): Unit =
        val stu1 = new Student()
        println(stu1.name)
        println(stu1.study())
        val stu2 = new Student("张三",20)
        println(stu2.name)
        println(stu1.study())
        stu2.age = 20
        println(stu2,age)
        println(stu2.banji)
        stu2.banji ="2班"  //报错
    }
}

2 . 类实例化

使用 new 关键字实例化一个类
var p = new Point() //不能省略() 
注意 var 和 val 实例的区别,val p修改变量对应对象值的内容,而不能更改对象的引用

3.类的继承

extends 关键字:子类继承父类
override 关键字:子类覆盖父类的方法

代码样例:
class XiaoXueShen extends Student{
    override def study()={
        print("我爱学习")
    }
    def play()={
        print("我爱打游戏")
    }
}
object Text02{
main(
  val xiao = new XiaoXueShen
    xiao.banji.prt //println(xiao.banji)
    xiao.study()
    xiao.play()
    xiao.isInstanceOf[Student] //true
  //isInstanceOf[T]判断是否为某一类型,asInstanceOf[T]进行强制类型转换。
    val x =new XiaoXueShen
    x.isInstanceOf[Studet]  //true    
  //Scala中的向上转型与向下转型与 Java 一样
)
}

4.抽象类

abstract修饰类,称为抽象类,未实现的方法称为抽象方法。抽象类不允许实例化

与 Java 类似,Scala 一个类中允许存在未实现的方法,此时应该使用 abstract修饰类,称为抽象类,未实现的方法称为抽象方法。抽象类不允许实例化。 
与 Java 不同的是,Scala 中的抽象方法不需要使用 abstract 修饰。 当子类重写父类方法时,使用 override 关键字,想要某个类成员不被子类重
写,可以给成员添加 final 修饰符实现。如果整个类都想有子类,直接将类修饰为 final class。 
子类中的方法要覆盖父类中的抽象方法,必须写 override。 
子类中的属性(val)要覆盖父类中的属性(val),必须写 override。 
注意,无论父类是抽象类还是普通类,使用 override 时不允许重写父类中的变量(var)。如下代码所示: 
abstract class Parent(var name:String){ 
  var age:Int=0 
  //省略方法声明 
} 
//运行错误 
class Child(override var name:String,override var age:Int) extends Parent(name) 
正确写法: 
class Parent(val name:String){ 
  val age:Int=0 
//省略方法定义 
} 
class Child(override val name:String,override val age:Int) extends Parent(name) 

代码样例

创建Shape.scala
//定义一个抽象类,使用abstract修饰
abstract class Shape{
	def draw():Unit
    def write()={
        print("我爱写代码")
    }
}
创建Circle.scala
class Circle extends Shape{
    override def draw():Unit={
    print("我爱小电影")
	}
}
object Test3{
    main{
        val circle = new Cricle
        circle.draw()
        circle.write()
    }
}

5.单例对象

5.1 object 与 class

Scala 比 Java 更加面向对象的特点之一是,取消了 static 关键字,因为类方法不是对象的方法。取而代之的是单例对象,具体是使用object代替class即可,object本身及其任何成员的定义与 `class`一致。 
本质上,单例对象是一种特殊的类,有且只有一个实例。单例对象在第一次
使用时被创建,单例对象不需要也不能再被实例化,单例对象中的所有成员都可以直接访问。应该注意到 main 方法必须定义在单例对象中。 
object HelloWorld { 
  println("我被创建") 
  def main(args: Array[String]): Unit = { 
    println("HelloWorld") 
  } 
println("我被执行") 
} 
通常也使用更简便的方式创建应用启动单例对象。 
object HelloWorld extends App{ 
  println("Hello World") 
} 
小结:在 Scala 中,类和单例对象的差别在于,单例对象不能带参数,而类可以。因为单例对象无法使用 new 关键字实例化,也就没有办法为它传递实例化参数
eg2:
object Blah {
  println("initalizing")
  def sum(l:List[Int]):Int=l.sum

  def main(args: Array[String]): Unit = {
    //单例对象可以通过单例对象名调用
    println(Blah.sum(List(1, 2, 3, 4, 5)))
  }
}

5.2 伴生

伴生(companion)
1.伴生关系 
限制条件:object(单例对象)与 class(普通类)同名且在一个文件中。例
如 Student.scala: 
//伴生类
class Student(n:String,a:Int){
    private var name = n //私有变量,伴生对象可以访问
    private var age = a
}
//伴生对象
object Student{
def apply(n:String,a:Int):Student = new Student(n,a)
main{
    val stu = Student("Jason",9) //通过伴生对象的apply()方法创建实列
    stu.name.prt
	}
}
一旦建立伴生关系,伴生对象与伴生类可相互访问私有成员。伴生对象主要为伴生类提供一种静态成员访问的途径。 

2.伴生类 
指在伴生关系中的伴生类,在 class(伴生类)中访问 object(伴生对象)的私有属性,使用“object.成员”。 
3.伴生对象 
指在伴生关系中的单例对象,在 object(伴生对象)中访问 class(伴生类)的私有属性,必须通过 class实例对象访问。如上面的“stu.name”。 

在 Scala 中,apply()方法有着特殊的含义。对于函数来说即调用函数自身。
例如: 
val f = (x: Int) => x + 1 
f.apply(1) 
f(1)  //等价于 f.apply(1) 
同样的,在单例对象中,以下写法也是 apply()方法的调用。 
def apply(n: String, a: Int): Student = new Student(n, a) 
val stu=Student("Jason",9) //等价于val stu=Student.apply("Jason",9) 
伴生对象可以继承伴生类,此时伴生类中的所有成员变为伴生对象中的静态成员,可通过伴生对象直接访问。如下代码所示。 
class Student(n: String, a: Int){ //伴生类 
  private var name = n 
  private var age = a 
  override def toString: String = { 
    s"name:$n,age:$a " 
  } 
} 
//伴生对象 
object Student extends Student("Jason",9){ 
  def main(args: Array[String]): Unit = { 
    println(Student.toString) 
  } 
} 

6.特质

特质封装了字段和方法的定义,并可以混入到类中重用。每个类只能继承一个超类,但可以混入多个特质。定义特质使用 trait关键字,其它与类定义相似,
使用 extends 或 with 关键字可将特质混入类中。对象也可以使用 with 混入特质,称为动态混入。 
特质像带有具体方法的 Java 接口,在特质中实现一次方法,而不再需要在每个混入特质的类中重新实现。 
特质也像抽像类,但抽象类只能继承一次,而特质可以混入多次
1.特质定义不能带有参数 
class Point(x:Int,y:Int) //编译通过 
trait NoPoint(x:Int,y:Int) //编译错误 
代码样例:
//创建一个PET特质
trait Pet{
    val name:String
    def cry():Unit
}
class Cat() extends Pet{
    override val name:String = "李四"
    override def cry():Unit={
        print("光棍")
    }
}
class Dog(val name:String) extends Pet{
    override def cry():Unit={
        print("狗")
    }
}
object Test6 extends App{
    val dog = new Dog("李四")
    print(dog.name)
    dog.cry()

    val cat =new Cat
    print(cat.name)
    cat.cry()
}
2.特质对 super 的动态调用 
class Root{ 
  def hello(){ 
    println("Hello,Root!") 
  } 
} 
class SubA extends Root{ 
  override def hello() {     
super.hello() //静态调用 
println("Hello,SubA!") 
  } 
} 
trait D extends Root{  //Root 是类,只能混入到 Root 及其子类中 
  def traitHello(){ 
    super.hello(); //动态调用 
  } 
}  
在 SubA 中,super 的调用是静态绑定的,父类 Root 的 hello()将被调用。而在 D 中,super 的调用是动态绑定的,即在定义特质 D 的时候,super 还不确定,直到特质被混入到具体类的时候才确定。 
val a=new Root with D //D 被混入 Root 时,super 为目标类 Root 
a.hello()    //调用 Root 类的 hello() 
又如: 
val a=new SubA with D //D 被混入 SubA 时,super 为目标类 SubA 
a.hello()    //调用 SubA 类的 hello() 
3.特质的构造顺序 
特质也可以有构造器,由字段的初始化和其他特质体中的语句构成。这些语
句在任何混入该特质的对象在构造时都会被执行。构造器的执行顺序: 
1)调用超类的构造器; 
2)特质构造器在超类构造器之后、类构造器之前执行; 
3)特质由左到右被构造; 
4)每个特质当中,父特质先被构造; 
5)如果多个特质共有一个父特质,父特质不会被重复构造 
6)所有特质被构造完毕,子类被构造。 
object MixinTrait extends App {
  val d = new D
  println(d.message)
  println(d.mes_toUpper)
}


//定义一个类
abstract class A{
  val message:String
}

//B继承抽象类A
class B extends A{
  override val message: String = "this is B"
}

//定义一个特质C,继承A
trait C extends A{
  def mes_toUpper=message.toUpperCase()
}

class D extends B with C
5.特质线性化 
线性化给了在多个特质中 super 被解析的顺序。假设有一个类 Cat,继承自
超类 Animal 以及两个特质 Furry 和 FourLegged。 FourLegged 又扩展了另一个
特质 HasLegs: 
class Animal  
trait Furry extends Animal  
trait HasLegs extends Animal  
trait FourLegged extends HasLegs  
class Cat extends Animal with Furry with FourLegged 
类 Cat 的继承层级和线性化次序展示在下图。继承次序使用传统的 UML 
标注指明:白色箭头表明继承,箭头指向超类型。黑色箭头说明线性化次序,箭
头指向 super 调用解决的方向

Cat 的线性化次序为:Cat>>FourLegged>>HasLegs>>Furry>>Animal。具体计算
方法:如果 C extends c1 with c2 with c3,则: 
lin(C) = C >> lin(c3) >> lin(c2) >> lin(c1) 
线性化第一个类是自己,这里“>>”意思是 “串接并去掉重复项, 右侧胜
出”,如上面的 Cat 类: 
class Cat extends Animal with Furry with FourLegged  
lin(Cat) = Cat>>lin(FourLegged)>>lin(Furry)>>lin(Animal)  
= Cat>>(FourLegged>>HasLegs)>>(Furry>>Animal)>>(Animal)  
= Cat>>FourLegged>>HasLegs>>Furry>>Animal 
6.特质调用链 
Scala 中支持让类或对象混入多个 trait 后,可依次调用多个 trait 中的同一个
方法,只要让多个 trait 中的同一个方法,在最后都依次执行 super 关键字即可。
类中调用多个 trait 中都有的这个方法时,首先会从最右边的 trait 的方法开始执
行,然后依次往左执行,形成一个调用链。如下所示。 
object TraitDemo2 { 
  def main(args: Array[String]): Unit = { 
    val t1 = new TraitTest with TraitA with TraitB 
    t1.log("msg...") 
  } 
} 

trait TraitSuper{ 
  def log(msg:String){ 
    println("TraitSuper 被调用") 
    println(msg) 
  } 
} 
trait TraitA extends TraitSuper{ 
  override def log(msg:String){ 
    println("TraitA 被调用") 
    super.log("TraitA: "+msg) 
  } 
} 
trait TraitB extends TraitSuper{ 
  override def log(msg:String){ 
    println("TraitB 被调用") 
    super.log("TraitB: "+msg) 
  } 
} 
class TraitTest  {} 
输出结果: 
TraitB 被调用 
TraitA 被调用 
TraitSuper 被调用 
TraitA: TraitB: msg... 
小结:越靠近后面的特质越优先起作用,当调用带混入的类的方法时,最右侧特质的方法首先被调用。如果那个方法调用了 super,则它调用其左侧特质的方法

内部类

一个类可以作为另一个类的成员,称为内部类
Scala内部类绑定到外部类的对象实例
class Graph {
  class Node {
    var connectedNodes: List[Node] = Nil
    def connectTo(node: Node) {
      if (connectedNodes.find(node.equals).isEmpty) {
        connectedNodes = node :: connectedNodes
      }
    }
  }
  var nodes: List[Node] = Nil
  def newNode: Node = {
    val res = new Node
    nodes = res :: nodes
    res
  }
}


val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
n1.connectTo(n2)      // legal
val h: Graph = new Graph
val n3: h.Node = h.newNode
n1.connectTo(n3)      // illegal!

7.依赖注入

1 .自身类型(self-type)也称自类型:this:T=> 其中,this 也可替换为 self 或其它不是关键字的别名。自身类型在特质和类中使用具不同含义。 
1.trait 中的自身类型 
在特质中表示指定了可以混入的类的超类,换句话说,当特质指定自身类型时,可以保证它只能混入该类的子类。示例: 
trait A {  
this: B=>  //指定了特质 A 可以混入 B 的子类,使得 A 拥有 B 的方法 
} 
class B 
class C extends B with A  //编译通过 
class D extends A   //编译错误 
 
2.class 中的自身类型 
在类中的自身类型使得类抽象化,该类在实例化时必须混入指定的特质。 
class A{ 
  this:B=>  //A 在实例化时必须混入 B 或 B 子特质,且不含抽象成员 
} 
trait B{ 
  val a:Int 
} 
trait C extends B{ 
  override val a: Int = 0 
} 

trait D 
//实例化 A 
val a1=new A with B  //编译错误,B 中含有抽象成员 
val a2=new A with C  //编译通过 
val a3=new A with D  //编译错误,D 不是 B 的子类型 
2 动态混入特质
格式: self=>  //注意与自身类型写法区别,this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名
其中“self”可以是除 this 外的任何非关键字,作用与 this 等价。 
class A {  
self => // this 别名 
val x = 2  
def foo = self.x + this.x 
} 

代码样例:动态混入

class Drawing {
  //实现注入
  self:Shaping=>
  def start():Unit=draw()

}
trait Shaping{
  def draw():Unit
}
trait DrawSquare extends Shaping{
  override def draw(): Unit = {println("drawing a square")}
}

trait DrawTriangle extends Shaping{
  override def draw(): Unit = {println("drawing a triangle")}
}

object Test08 extends App {
   val d1 = new Drawing with DrawSquare
  d1.start()
    
  val d2 = new Drawing with DrawTriangle
  d2.start()
}

依赖注入

依赖注入所做的事情就是通过构造器或 setter 方法将依赖对象(当前对象所
依赖的对象)注入到当前对象中,实现组件间的解耦。关键点是依赖对象的创建
由第三方完成。 
在 Scala 中可以通过自身类型实现依赖注入。下面示例是自身类型在依赖注
入中的应用。 
trait Logger { 
  def log(msg: String) 
} 
trait Auth { 
  auth: Logger => // 别名 auth 
  def act(msg: String) { 
    //调用被依赖的对象 Logger 的 log 方法 
    auth.log(msg) 
  } 
} 
object DI extends Auth with Logger { 
  //重写父类的方法 
  override def log(msg: String) = println(msg); 
} 
object DependencyInjection { 
  def main(args: Array[String]) { 
    // DI 为静态对象,执行结果:dependency injection 
    DI.log("dependency injection") 
  } 
} 

8、样例类

 
使用 case 修饰符的类称为样例类(case class)。 case 修饰符可以让 Scala 编
译器自动为类添加一些便捷设定。 
case class Student(name:String,age:Int)      //定义样例类 
val stu=Student("Jason",19)      //创建样例类的实例,无需 new 关键字 
println(stu.name)       //访问对象属性   
1.自动生成类的 getter 和 setter 访问器 
1)构造器中的参数默认获得“val”前缀,只生成 getter 
2)可手动显式对参数指定“var”前缀,同时生成 getter&setter 
2.自动生成类的伴生对象(单例对象) 


在单例对象中实现 apply()方法,如下所示: 
def apply(n: String, a: Int): Student = new Student(n, a) 
所以可以通过类名“Student("Jason",19)”创建 Student 对象,这也是样例类
实例常用创建方式。 
另外实现了一些其它的方法:toString()、equals()、copy()、hashCode()、unapply()
等。特别地 unapply()也称为提取器,在后面的模式匹配详细介绍。 
3.样例类与普通的区别: 
1)样例类通常用于描述不可变的数据,数据完全依赖构造参数。如果一个
对象在内部执行有状态计算,或者表现出其他类型的复杂行为,那么它应该是一
个普通类。 
2)样例类默认可用伴生对象方式创建实例,普通类需要定义 apply()。 
3)样例类默认支持模式匹配,普通类需要定义 unapply()。 
总的来说,样例类本质还是方便我们创建、操作的普通类而已,可以说编译
器以极小的转换为我们带来了极大的便利,代价便是使用 case 修饰符以及类和
对象会变得稍微大一些。变大的原因是产生了附加的方法以及对每个构造器参数
添加了隐含的前缀,不过样例类最大的好处还是支持模式匹配

//样例类和枚举
创建CaseClassDemo.scala
object CaseClassDemo {
  def main(args: Array[String]): Unit = {
    val zhangsan = Student("zhangsan",18)
    println(zhangsan.age)
  }

}
case class Student(name:String,age:Int)

创建Weekday.scala
object Weekday extends Enumeration {

  //赋值
  //枚举值从0开始计数的
  val Mon,Tue,Wen,Thu,Fri,Sat,Sun=Value

}

object Test09{
  def main(args: Array[String]): Unit = {
    println(Weekday.Mon)
    println(Weekday.Mon.id)
    Weekday.values.foreach(println)
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值