大数据——Scala面向对象编程

it2026-04-15  1

Scala面向对象编程

Scala面向对象基本概念类(class)类成员访问修饰符类的定义类的继承 抽象类单例对象(object)定义单例对象伴生(Companion)伴生类与伴生对象 特质(trait)使用特质混入特质(mixin)动态混入特质特质与抽象类的选择内部类 样例类样例类和枚举样例类与普通类泛型类类型边界 型变包与包对象包引用

Scala面向对象基本概念

类(Class) 抽象类(abstract class) 单例对象(object) 特质(trait) 样例类(case class)

类(class)

类通过class关键字定义 类通过new关键字创建实例 类拥有成员变量和方法 类的成员默认为public,也支持private、protected 类中无法定义静态成员变量和方法 类无需明确定义构造方法,通过构造参数列表声明为类的一部分

类成员访问修饰符

Java

ModifierlClassPackageSubclassWorldpublicYYYYprotectedYYYNdefaultYYNNprivateYNNN

Scala

ModifierlClassCompanionSubclassPackageWorlddefaultYYYYYprotectedYYYNNprivateYYNNN

类的定义

构造器

主构造器辅助构造器

成员变量与方法

//主构造器执行类定义中的所有语句 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) } } var bp=new BlackPoint() bp.x bp.y bp.move(1,2)

抽象类

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

子类重写父类抽象方法时,"overwrite"关键字可选 abstract class Shape{ def draw():Unit } 子类重写父类非抽象方法时,"overwrite"关键字必选 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()接受一个对象,从对象中提取出相应的值,主要用于模式匹配中。

样例类和枚举

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