Scala-基本语法之面向对象(OOP)

  • 类通过class关键字定义
  • 类通过new关键字创建实例
  • 类拥有成员变量和方法
  • 类的成员默认为public,也支持private,protected
  • **类中无法定义静态成员变量和方法**
  • 类无需明确定义构造方法,通过构造参数列表声明为类的一部分
    1)基本语法
    [修饰符] class 类名 {
    类体
    }
    说明
    (1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
    (2)一个Scala源文件可以包含多个类

构造器

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala类的构造器包括:主构造器和辅助构造器
1)基本语法

//主构造器执行类定义中的所有语句
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
  }  
}

(1)辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数来区分。
(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

构造器参数

Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
(1)未用任何修饰符修饰,这个参数就是一个局部变量
(2)var修饰参数,作为类的成员属性使用,可以修改
(3)val修饰参数,作为类只读属性使用,不能修改

类的继承

Scala使用==“extends”关键字实现继承
子类重写父类方法必须使用
“override”==关键字
scala是单继承

class BlackPoint() extends Point {
    private var color="black"   
    override def move(dx: Int, dy: Int) ={
    x = x + dx
    y = y + dy
    println("moved to x:"+x+" y:"+y)
  }  
}

抽象类

抽象类可包含未实现的方法,即抽象方法
抽象类无法实例化
抽象类使用“abstract”关键字修饰

  • 子类重写父类抽象方法时,“override”关键字可选
  • 子类重写父类非抽象方法,“override”关键字必写
abstract class Shape{
    def draw():Unit
}


class Square extends Shape{
    override def draw():Unit={
        println("draw a square")
    }
}
var shape=new Square
shape.draw

(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。
(3)子类中调用夫类的方法使用super关键字
(4)属性重写只支持val类型,而不支持var。
(5)scala中属性和方法都是动态绑定,而java中只有方法为动态绑定。

匿名子类

1)说明
Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。
2)案例实操

abstract class Person {

  val name: String

  def hello(): Unit
}

object Test {

  def main(args: Array[String]): Unit = {

    val person = new Person {

      override val name: String = "teacher"

      override def hello(): Unit = println("hello teacher")
    }
  }
}

单例对象(object)

Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

  • 使用“object”关键字声明,可包含变量、方法与代码定义
  • 单例对象中的成员变量、成员方法通过单例对象名直接调用
  • 单例对象第一次被访问时初始化,并执行全部代码块
  • 单例对象不能new,且无构造参数
  • 程序入口main()方法必须定义在单例对象中
  • 单例对象与同名类定义在同一文件中时形成绑定关系
//Blah.scala
package test  //定义包名
//定义单例对象
object Blah {
  println("Blah initializing...")
  def sum(l: List[Int]): Int = l.sum
}
test.Blah.sum(List[Int](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 {
  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)
  }
}

apply方法

(1)通过伴生对象的apply方法,实现不使用new方法创建对象。
(2)如果想让主构造器变成私有的,可以在()之前加上private。
(3)apply方法可以重载。
(4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以同一面向对象编程和函数式编程的风格。

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)
  }
}

特质

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()})

混入特质(mixin)

当某个特质被用于组合类时,被称为混入
一个类只能有一个父类但是可以有多个混入(分别使用关键字extends和with)

abstract class A {
  val message: String
}
class B extends A {
  val message = "I'm an instance of class B"
}
trait C extends A {
  def loudMessage = message.toUpperCase()
}
class D extends B with C //构造顺序由左往右,如果前面已经构造了某个父类,后面子类的该父类不会重复构造


val d = new D
println(d.message)  // I'm an instance of class B
println(d.loudMessage)  // I'M AN INSTANCE OF CLASS B

动态混入特质

class Drawing { 
  self: Shape =>    //this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名。

  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()

特质与抽象类的选择

优先使用特质

  • 抽象类只能继承一次
  • 特质可混入多个
    需要使用带参构造方法时,使用抽象类
    与Java互操作性
  • 抽象类与Java完全可互操作
  • 特质只有在不包含任何实现代码时才可互操作

内部类

一个类可以作为另一个类的成员,称为内部类

  • Java内部类是外部类的成员
  • Scala内部类绑定到外部类的对象实例
class Graph {
  class Node {
    var connectedNodes: List[Graph#Node] = Nil
    def connectTo(node: Graph#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! n1与n3被认为是不同的类型


样例类(case class)

样例类常用于描述不可变的值对象(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() //unapply()接受一个对象,从对象中提取出相应的值,主要用于模式匹配中。

样例类与枚举

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

//枚举的使用
Weekday.Sun
Weekday.Sun.id //获取枚举值的计数值
Weekday.values.foreach(println)

  • 样例类与枚举区别
    枚举更简单,代码更少
    样例类的字段比枚举的值更强大
    样例类可扩展
abstract class Term(code: String)
case class Var(name: String) extends Term(name)
case class Fun(arg: String, body: Term) extends Term(arg)
case class App(f: Term, v: Term) extends Term("App")

  • 样例类与普通类
    区别
  • 样例类通常用于描述不可变的数据,数据完全依赖构造参数
  • 样例类默认不可变,通过模式匹配可分解
  • 两个样例类“==”操作时,通过按值比较而不是按引用
  • 样例类操作更简单
    最佳实践
  • 如果一个对象在内部执行有状态计算,或者表现出其他类型的复杂行为,那么它应该是一个普通类

泛型类

泛型类指可以接受类型参数的类,泛型类在集合类中被广泛使用
与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] // 不变类
    默认情况下,Scala中的泛型类是不变的

包与包对象

Scala包:package 包名

  • 只能包含数字、字母、下划线、圆点
  • 不能用数字开头, 不能使用关键字
  • 可以在同一个.scala文件中,声明多个并列的package
package com.kgc{
package scala1 {... }
package scala2 {... }
}
package scala3 {... }

Scala包对象

  • 包可以包含类、对象和特质,但不能包含变量或方法的定义,应使用包对象解决这个问题
package com.kgc{
package object scala {	//对应包com.kgc.scala,每个包都可以有一个包对象
    val name="Wow"
}...
package scala{...}//与包对象同名的包可直接使用包对象中定义的变量和方法
}

包引用

import让包和包对象的成员可以直接通过名称访问

//易于访问Fruit
import cn.kgc.Fruit
//易于访问cn.kgc的所有成员
import cn.kgc._
//易于访问cn.kgc.Fruits的所有成员
import cn.kgc.Fruits._
//只引用Apple与Orange,并且Apple重命名为McIntosh
import cn.kgc.Fruits.{Apple=>McIntosh,Orange}

def showFruit(fruit:Fruit){
    import fruit._
    println(name+color)
}

import灵活引用

  • 可以出现在任何地方
  • 可以是对象和包
  • 可以重命名或隐藏一些被引用的成员
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值