Scala面向对象基本概念
-
类
- 类通过class关键字定义
- 类通过new关键字创建实例
- 类拥有成员变量和方法
- 类的成员默认为public,也支持private,protected
- 类中无法定义静态成员变量和方法
- 类无需明确定义构造方法,通过构造参数列表声明为类的一部分
-
类成员访问修饰符
-
java
Modifier class package subclass world public Y Y Y Y protected Y Y Y N default Y N N N private Y N N N -
scala
Modifier class 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关键字实现继承
-
子类重写父类方法必须使用override关键字
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) } } var bp=new BlackPoint() bp.x bp.y bp.move(1,2)
-
-
抽象类
-
抽象类可包含未实现的方法,即抽象方法
-
抽象类无法实例化
-
抽象类使用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
-
-
-
单例对象
-
scala的类中无法定义静态成员,即无static关键字
-
使用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))
-
-
伴生
-
单例对象与同名类定义在同一文件中时形成绑定关系
- 同名类称为单例对象的伴生类
- 单例对象称为同名类伴生对象
-
伴生类与伴生对象可相互访问各自私有成员
-
伴生对象可为伴生类增加静态成员
//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) } }
-
-
特质
-
scala中没有接口的概念
-
特质用于在类之间共享程序接口和字段,类似java接口
-
特质是字段和方法的集合,可以提供字段和方法实现
-
类和单例对象都可以扩展特质
-
特质不能实例化,因此没有构造参数,类似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)
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 } } 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!
-
-
-
样例类
-
样例类常用于描述不可变的值对象
case class Student(name:String,age:Int) //定义样例类 val stu=Student("Jason",19) //创建样例类的实例,无需new关键字 println(stu.name) //访问对象属性
- 样例类构造参数默认声明为val,自动实现类构造参数getter
- 样例类构造参数声明为var时,自动实现类构造参数setter和getter
- 阳历类自动创建伴生对象
- 阳历类自动实现的其他方法
- toString90,equals(),copy(),hashCode()
- 伴生对象中的apply(),unapply()
-
枚举
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[T]
- 默认情况下,scala中的泛型类是不变的
- 协变 class Foo[+T]
-
包与包对象
-
scala包:package 包名
-
只能包含数字,字母,下划线,圆点
-
不能用数字开头,不能使用关键字
-
可以在同一个.scala文件中,声明多个并列的package
package com.233{ package scala1 {... } package scala2 {... } } package scala3 {... }
-
-
scala包对象
-
包可以包含类,对象和特质,但不能包含变量或方法的定义,应使用包对象解决这个问题
package com.233{ package object scala { //对应包com.233.scala,每个包都可以有一个包对象 val name="Wow" }... package scala{...}//与包对象同名的包可直接使用包对象中定义的变量和方法 }
-
-
包引用
-
import让包和包对象的成员可以直接通过名称访问
//易于访问Fruit import 233.Fruit //易于访问233的所有成员 import 233._ //易于访问233.Fruits的所有成员 import 233.Fruits._ //只引用Apple与Orange,并且Apple重命名为McIntosh import 233.Fruits.{Apple=>McIntosh,Orange} def showFruit(fruit:Fruit){ import fruit._ println(name+color) }
-
import灵活引用
- 可以出现在任何地方
- 可以是对象和包
- 可以重命名或隐藏一些被引用的成员
-
-