Scala【三】 面向对象入门

Scala面向对象基本概念

1. 类

  • 类通过class关键字定义
  • 类通过new关键字创建实例
  • 类拥有成员变量和方法
  • 类的成员默认为public,也支持private、protected
  • 类中无法定义静态成员变量和方法
  • 类无需明确定义构造方法,通过构造参数列表声明为类的一部分
类成员访问修饰符
  • java
ModifierClasspackageSubclassWorld
publicYYYY
protectedYYYN
defaultYYNN
privateYNNN
  • Scala
ModifierClassCompanionSubclassPackageWorld
defaultYYYYY
protectedYYYNN
privateYYNNN
类的定义
  • 构造器 - 主构造器 - 辅助构造器
  • 成员变量与方法
//主构造器执行类定义中的所有语句
class Point(xc: Int, yc: Int) {
  var x: Int = xc	//成员变量
  var y: Int = yc 
  //辅助构造器
  def this() ={
    this(0, 0)	//第一句必须调用其他构造器
  }
  //成员方法
  def move(dx: Int, dy: Int) ={
    x = x + dx
    y = y + dy
  }  
}
  • 类的实例化
var p=new Point()
p.x
p.y
p=new Point(12,11)
p.x
p.y
p.move(1,2)

例:

class Student {
  //定义成员变量
    //var 相当于自动生成getter和setter方法,val只有getter方法
  var name = "justin"
  var age:Int=_   //0
  var gender:String = _  //null
  val classNo = 1
  //定义一个辅助构造器1
  def this(name:String,age:Int)={
    //辅助构造器必须从调用其他构造器开始
    this()
    this.name=name
    this.age=age
  }

  //定义一个辅助构造器2
  def this(gender:String)={
    this("lisi",18)
    this.gender=gender
  }

  //  定义一个成员方法
  def study()={
    println("good good study,day day up")
  }
}

object Test01{
  def main(args: Array[String]): Unit = {
    //通过主构造器
    val stu1 = new Student()
//    println(stu1.name)    //justin
//    println(stu1.gender)  //null
//    println(stu1.age)     //0
//    stu1.study()

    //通过辅助构造器
    val stu2 = new Student("zhangsan",20)
    println(stu2.name)    //zhangsan
    println(stu2.gender)  //null
    println(stu2.age)     //20
    stu2.study()
    println(stu2.classNo)  //1
//    stu2.classNo=2   val没有set方法,报错
  }
}

或者在主构造器中直接定义成员变量

class Person(var name:String,var age:Int) {
  
}
类的继承
  • Scala使用“extends”关键字实现继承
  • 子类重写父类方法必须使用“override”关键字

如,继承上面缩写的Student类


class LittleStudent extends Student{
  override def study(): Unit = {
    println("little students hate studying")
  }

  def play ={
    println("little students like playing games")
  }

}

object Test02{
  def main(args: Array[String]): Unit = {
    val little = new LittleStudent
    println(little.classNo)     //1
    little.study()              //little students hate studying
    little.play                 //little students like playing games
  }
}

抽象类继承,子类调用包含父类成员变量方法和自己的方法
在这里插入图片描述

抽象类
  • 抽象类可包含未实现的方法,即抽象方法
  • 抽象类无法实例化
  • 抽象类使用“abstract”关键字修饰
    • 子类重写父类抽象方法时,“override”关键字可选
    • 子类重写父类非抽象方法,“override”关键字必写

2.抽象类

abstract class Shape {
  def draw():Unit

  def write()={
    println("Write Something")
  }
}

​ 继承抽象类:

class Circle extends Shape {
  def draw(): Unit = {
    println("draw Circle")
  }
}

object Test03{
  def main(args: Array[String]): Unit = {
    val circle = new Circle
    circle.draw()         //draw Circle
    circle.write()        //Write Something
  }
}

3.单例对象

  • Scala的类中无法定义静态成员,即无static关键字。如何像Java一样表达类的静态成员变量、成员方法与静态代码块?
  • Scala解决方案:单例对象
    • 使用“object”关键字声明,可包含变量、方法与代码定义
    • 单例对象中的成员变量、成员方法通过单例对象名直接调用
    • 单例对象第一次被访问时初始化,并执行全部代码块
    • 单例对象不能new,且无构造参数
    • 程序入口main()方法必须定义在单例对象中
    • 单例对象与同名类定义在同一文件中时形成绑定关系
定义单例对象
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)))
  }
}
伴生类与伴生对象
  • 单例对象与同名类定义在同一文件中时形成绑定关系
    • 同名类称为单例对象的伴生类(class)
    • 单例对象称为同名类伴生对象(object)
  • 伴生类与伴生对象可相互访问各自私有成员
  • 伴生对象可为伴生类增加静态成员
//Student.scala
//伴生类
class Student(n: String, a: Int) {
  private var name = n    //私有变量,伴生对象可以访问
  private var age = a
}
//伴生对象
object Student {
    //使用伴生对象的apply()方法省略掉new关键字。Student.apply()等价于Student()
  def apply(n: String, a: Int): Student = new Student(n, a)
  def main(args: Array[String]): Unit = {
    val stu=Student("Jason",9)  //通过伴生对象的apply()方法创建实例
    println(stu.name)
  }
}

4. 特质(trait)

  • Scala中没有接口(interface)的概念
  • 特质用于在类之间共享程序接口和字段,类似Java接口
  • 特质是字段和方法的集合,可以提供字段和方法实现
  • 类和单例对象都可以扩展特质(extends)
  • 特质不能被实例化,因此没有构造参数,类似Java接口
  • 特质使用“trait”关键字定义
  • 实现特质中的方法使用“override”
特质示例
import scala.collection.mutable.ArrayBuffer

trait Pet {
  val name: String
  def cry():Unit
}
class Dog(val name: String) extends Pet{
  override def cry()=println("wow ...")
}
val dog = new Dog("Harry")
val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.foreach(pet => {println(pet.name);pet.cry()})  // Prints Harry wow ...
混入特质
  • 当某个特质被用于组合类时,被称为混入
  • 一个类只能有一个父类但是可以有多个混入(分别使用关键字extends和with)
object MinxinTrait {
  def main(args: Array[String]): Unit = {
    val d = new D
    println(d.message)
    println(d.mess_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 mess_toUpper=message.toUpperCase()

}

//构造顺序由左往右,如果前面已经构造了某个父类,后面子类的该父类不会重复构造
class D extends B with C
动态混入特质
class Drawing { 
 //this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名。

  self: Shape =>
  def start(): Unit = draw()
}
trait Shape {
  def draw(): Unit
}
trait Square extends Shape {
  def draw(): Unit = println("draw a square")
}
trait Triangle extends Shape {
  def draw(): Unit = println("draw a triangle")
}
//动态混入
(new Drawing() with Square).start()
(new Drawing() with Triangle).start()

5. 内部类

  • 一个类可以作为另一个类的成员,称为内部类
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
  }
}

object Test{
   def main(args: Array[String]): Unit = {
    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!
   }
}


6. 样例类

  • 样例类常用于描述不可变的值对象(Value Object)

    case class Student(name:String,age:Int)      //定义样例类
    val stu=Student("Jason",19)      //创建样例类的实例,无需new关键字
    println(stu.name)       //访问对象属性    
    
    • 样例类构造参数默认声明为“val”,自动实现类构造参数的getter
    • 样例类构造参数声明为“var”时,自动实现类构造参数的setter和getter
    • 样例类自动创建伴生对象
    • 样例类自动实现的其他方法
      • toString()、equals()、copy()、hashCode()
      • 伴生对象中的apply()、unapply()

7. 枚举

  • 枚举(Enumeration)
object Weekday  extends Enumeration {
  //枚举值从0开始计数
  val Mon,Tue,Wed,Thu,Fri,Sat,Sun=Value
}


//枚举的使用
Weekday.Sun
Weekday.Sun.id // 获取枚举值的计数值 6 是从0开始计数
Weekday.values.foreach(println)

8. 泛型类

  • 泛型类指可以接受类型参数的类,泛型类在集合类中被广泛使用
  • 与Java不同,定义泛型类使用“[]”
class Stack[T] {
    var elements: List[T] = Nil
    def push(x: T) { elements = x :: elements }
    def top: T = elements.head
    def pop() {
      var t = elements.head
      elements = elements.tail
      t
    }
    def showElements(){
      elements.foreach(x=>print(s"$x "));println()}
  }


val ms = new Stack[Int]()
ms.push(10)
ms.showElements()
ms.push(20)
ms.showElements()
val t = ms.pop()
ms.showElements()
类型边界
  • 在Scala中,类型参数可以有一个类型边界约束
  • 类型上界:将类型限制为另一种类型的子类
  • T<:A 表示类型变量T应该是类型A的子类
  • A是具体类型,T是泛型
  • 类型下界:将类型声明为另一种类型的超类
  • T>:A 表示类型变量T应该是类型A的超类
  • A是具体类型,T是泛型
型变
  • 协边

    class Foo[+T] // 协变类

    • 对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Foo[A] 就是 Foo[B] 的子类型
  • 逆变

    class Bar[-T] // 逆变类

    • 对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Bar[B] 就是 Bar[A] 的子类型
  • 不变

    class Baz[T] // 不变类

showElements()
val t = ms.pop()
ms.showElements()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值