Scala面向对象基本概念
类(Class)
抽象类(abstract class)
单例对象(object)
特质(trait)
样例类(case class)
类(class)
类通过class关键字定义
类通过new关键字创建实例
类拥有成员变量和方法
类的成员默认为public,也支持private、protected
类中无法定义静态成员变量和方法
类无需明确定义构造方法
Scala
Modifier | lClass | Companion | Subclass | Package | World |
---|---|---|---|---|---|
default | Y | Y | Y | Y | Y |
protected | Y | Y | Y | N | N |
private | Y | Y | N | N | N |
类的定义
构造器
- 主构造器
- 辅助构造器
成员变量与方法
//主构造器执行类定义中的所有语句
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)
类的继承
Scala使用"extends"关键字是实现继承
子类重写父类方法必须使用"overwrite"关键字
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"关键字修饰
- 子类重写父类抽象方法时,"overwrite"关键字可选
abstract class Shape{
def draw():Unit
}
class Square extends Shape{
override def draw():Unit={
println("draw a square")
}
}
var shape=new Square
shape.draw
单例对象(object)
-
问题
Scala的类中无法定义静态成员,即无static关键字
。如何像Java一样表达类的静态成员变量、成员方法与静态代码块?
Scala解决方案:单例对象
使用“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))
伴生(Companion)
单例对象与同名类定义在同一文件中时形成绑定关系
- 同名类称为单例对象的伴生类(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)
}
}
特质(trait)
Scala中没有接口(interface)的概念
特质用于在类之间共享程序接口和字段,类似于Java接口
特质是字段和方法的集合,可以提供字段和方法实现
类和单例对象都可以扩展特质(extends)】
特质不能被实例化,因此没有构造参数,类似于Java接口
特质使用“trait”关键字定义
实现特质中的方法使用“overwrite”
使用特质
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 ...
混入特质(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[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
}
}
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!
样例类
样例类常用于描述不可变的值对象(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()接受一个对象,从对象中提取出相应的值,主要用于模式匹配中。
泛型类
泛型类指可以接受类型参数的类,泛型类在集合类中被广泛使用
与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] // 协变类
class Bar[-T] // 逆变类
class Baz[T] // 不变类
包与包对象
Scala包:package包名
- 只能包含数字、字母、下划线、圆点
- 不能用数字开头,不能使用关键字
- 可以在同一个.scala文件中,声明多个并列的package
package com.kgc{ package object scala { //对应包com.kgc.scala,每个包都可以有一个包对象 val name="Wow" }... package scala{...}//与包对象同名的包可直接使用包对象中定义的变量和方法 }