Scala 面向对象(OOP)详解

it2024-01-17  67

Scala面向对象编程综述

1.Scala是纯粹的面向对象的语言 Scala 是纯粹的面向对象的语言,每个值都是对象,每个操作都是方法调用。 2.Scala大量重用了Java中的类型 Scala 与 Java 完全兼容,Scala 程序会被编译成 Java 字节码,访问 Java 字段,继承 Java 类,实现 Java 接口都不需要特别的语法。 3…Scala OOP 比较 Java OOP 的主要差异 (1)Scala取消了接口(Interface),新增了类似的特质概念(Trait)。 (2)Scala 取消静态字段和方法,新增单例对象的概念(Singleton Object)。 (3)Scala 新增了样例类(case class)

Scala类和对象

类是对对象的抽象模板,描述了该类对象可以拥有的属性和方法 对象是类的具体实例。 1.类的基本概念 (1)类通过class关键字定义 (2)类通过new关键字创建实例 (3)类拥有成员变量和方法 (4)类的成员默认为public,也支持private、protected (5)类中无法定义静态成员变量和方法 (6)类无需明确定义构造方法,通过构造参数列表声明为类的一部分

2.类的定义 (1)构造器 所有构造器都没有返回值。 ①主构造器 主构造器便是“类名(构造参数列表)”

//主构造器的参数列表直接定义在类名后面 class Point(xc: Int, yc: Int) { var x: Int = xc //成员变量 var y: Int = yc

②辅助构造器 辅助构造器“this(构造参数列表)”,类似于 Java 中的构造方法重载 注意辅助构造器必须间接或直接调用主构造器。

def this() ={ this(0, 0) //第一句必须调用其他构造器 }

(2)成员变量和方法 ①类中可以有字段和方法,统称为成员变量。字段可以用 var 或 val 定义,都是指向对象的变量,var 字段表示可以重新赋值,val 不能。

//成员方法 def move(dx: Int, dy: Int) ={ x = x + dx y = y + dy } }

②在方法不需要参数时可以定义成下面两种方法: 对于无参数方法的方法形式 obj.width 又像是在直接引用 obj 对象的width 属性,这种统一性就叫做统一访问原则。

def width():Int=x //空括号方法,调用方式:obj.width 或者 obj.width() def width:Int=x //无参数方法,调用方式:obj.width

③在 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 的 }

(3)类的实例化 使用 new 关键字实例化一个类。

var p=new Point() p.x p.y p=new Point(12,11) p.x p.y p.move(1,2)

(4)类的继承 ①Scala使用“extends”关键字实现继承 ②子类重写父类方法必须使用“override”关键字

class Person { override def toString = getClass.getName +"name=" } class Employee extends Person { override def toString = super.toString } val p=new Person p.name="zhangsan" println(p.toString) val e=new Employee e.name="lisi" println(e.toString) println(p.isInstanceOf[Person]) //true println(p.isInstanceOf[Employee]) //false println(e.isInstanceOf[Person]) //true println(e.isInstanceOf[Employee]) //true p.asInstanceOf[Employee] //编译失败

isInstanceOf[T]判断是否为某一类型,asInstanceOf[T]进行强制类型转换。 ③向上转型

def todo(x:Person)={ println(x.toString) } todo(p) //编译通过 todo(e) //编译通过

④向下转型

def todo(x:Person)={ println(x.asInstanceOf[Employee].toString) } todo(p) //编译失败 todo(e) //编译通过

3.抽象类 (1)抽象类可包含未实现的方法、即抽象方法 (2)抽象类无法实例化 (3)抽象类使用“abstract”关键字修饰 ①子类重写父类抽象方法时,“override”关键字可选

abstract class Shape{ def draw():Unit }

②子类重写父类非抽象方法,“override”关键字必写

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

4.单例对象 (1)Object与class ①单例对象是一种特殊的类,有且只有一个实例 ②单例对象在第一次使用时被创建,单例对象不需要也不能再被实例化 ③单例对象中的所有成员都可以直接访问,main 方法必须定义在单例对象中。

object HelloWorld { println("我被创建") def main(args: Array[String]): Unit = { println("HelloWorld") } println("我被执行") }

简单方式创建:

object MyApp extends App{ println("Hello World") }

因为所继承的 App(内置特质)中已经提供了 main()方法的实现。

④类和单例对象的区别: 单例对象不能带参数,而类可以。因为单例对象无法使用 new 关键字实例化,也就没有办法为它传递实例化参数。

(2)伴生(companion) ①伴生关系 限制条件:object(单例对象)与 class(普通类)同名且在一个文件中。 一旦建立伴生关系,伴生对象与伴生类可相互访问私有成员。伴生对象主要为伴生类提供一种静态成员访问的途径

//伴生类 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) } }

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

④apply()的方法的调用

def apply(n: String, a: Int): Student = new Student(n, a) val stu=Student("Jason",9) //等价于 val stu=Student.apply("Jason",9)

5.特质(Trait) (1)概述: ①Scala中没有接口(interface)的概念 ②特质用于在类之间共享程序接口和字段,类似Java接口 ③特质是字段和方法的集合,可以提供字段和方法实现 ④类和单例对象都可以扩展特质(extends) ⑤特质不能被实例化,因此没有构造参数,类似Java接口 ⑥实现特质中的方法使用“override”

(2)使用特质

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 ...

(3)混入特质(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

(4)动态混入特质

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)特质与抽象类的选择 ①优先使用特质 抽象类只能继承一次 特质可混入多个 ②需要使用带参构造方法时,使用抽象类 ③与Java互操作性 抽象类与Java完全可互操作 特质只有在不包含任何实现代码时才可互操作

6.内部类 一个类可以作为另一个类的成员,称为内部类 (1)Java内部类是外部类的成员

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

(2)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与n3被认为是不同的类型 n1.connectTo(n3) // illegal!

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

case class Student(name:String,age:Int) //定义样例类 val stu=Student("Jason",19) //创建样例类的实例,无需new关键字 println(stu.name) //访问对象属性

(1)样例类构造参数默认声明为“val”,自动实现类构造参数的getter (2)样例类构造参数声明为“var”时,自动实现类构造参数的setter和getter (3)样例类自动创建伴生对象 (4)样例类自动实现的其他方法: ①toString()、equals()、copy()、hashCode() ②伴生对象中的apply()、unapply():unapply()接受一个对象,从对象中提取出相应的值,主要用于模式匹配中。 (5)样例类与普通的区别: ①样例类通常用于描述不可变的数据,数据完全依赖构造参数。 ②样例类默认可用伴生对象方式创建实例,普通类需要定义 apply()。 ③样例类默认支持模式匹配,普通类需要定义 unapply()。 ④两个样例类“==”操作时,通过按值比较而不是按引用 ⑤样例类操作更简单 8.样例类与枚举 (1)枚举(Enumeration)的定义

object Weekday extends Enumeration { //枚举值从0开始计数 val Mon,Tue,Wed,Thu,Fri,Sat,Sun=Value } //枚举的使用 Weekday.Sun Weekday.Sun.id //获取枚举值的计数值 Weekday.values.foreach(println)

(2)样例类与枚举区别 ①枚举更简单,代码更少 ②样例类的字段比枚举的值更强大 ③样例类可扩展

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

9.泛型类 (1)泛型类指可以接受类型参数的类,泛型类在集合类中被广泛使用 (2)定义泛型类使用“[]”

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

10.类型边界 (1)在Scala中,类型参数可以有一个类型边界约束 (2)类型上界:将类型限制为另一种类型的子类 ①T<:A 表示类型变量T应该是类型A的子类 ②A是具体类型,T是泛型

abstract class Animal { def name: String } abstract class Pet extends Animal {} class Cat extends Pet { override def name: String = "Cat" } class Dog extends Pet { override def name: String = "Dog" } class Lion extends Animal { override def name: String = "Lion" } class PetContainer[P <: Pet](p: P) { def pet: P = p } val dogContainer = new PetContainer[Dog](new Dog) //编译通过 val catContainer = new PetContainer[Cat](new Cat) //编译通过 val lionContainer = new PetContainer[Lion](new Lion) //编译错误

(3)类型下界:将类型声明为另一种类型的超类 ①T>:A 表示类型变量T应该是类型A的超类 ②A是具体类型,T是泛型

abstract class Animal { def name: String } abstract class Pet extends Animal {} class Cat extends Pet { override def name: String = "Cat" } class Dog extends Pet { override def name: String = "Dog" } class Lion extends Animal { override def name: String = "Lion" } class PetContainer[P >: Lion](p: P) { def pet: P = p } val dogContainer = new PetContainer[Dog](new Dog) //编译错误 val catContainer = new PetContainer[Cat](new Cat) //编译错误 val lionContainer = new PetContainer[Lion](new Lion) //编译通过

11.型变 型变定义了泛型的继承方式 (1)协变 List 类型是协变类型 对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Foo[A] 就是 Foo[B] 的子类型

class Foo[+T] // 协变类

(2)逆变 对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Bar[B] 就是 Bar[A] 的子类型

class Bar[-T] // 逆变类

(3)不变 默认情况下,Scala中的泛型类是不变的

class Baz[T] // 不变类
最新回复(0)