Scala 类和对象
友情推荐
参考
Scala 参考文档 (scala-lang.org)
[Scala 教程 ](
类和对象
Scala 不论是类、还是抽象类、还是特质,都是使用 extends
关键字
创建类
// 第一种
// 如果类是空的,没有任何成员,可以省略{}
class Student
// 第二种
class Teacher {
//
}
创建对象
// 第一种
// 如果构造器的参数为空,可以省略()
val stu1 = new Student()
// 第二种
val stu2 = new Student
成员变量的定义和访问
说明
① 在类中使用 var/val
来定义成员变量
② 对象直接使用成员变量名称来访问成员变量
定义成员变量
// 第一种:scala 类型自动推断,并设置初始化值
class Student {
var name = ""
var age = 0
}
// 第二种
// 使用下划线进行初始化
class Teacher {
var name: String = _
var age: Int = _
}
访问成员变量
val teacher1 = new Teacher()
teacher1.name = "Jack"
println(teacher1.name)
println(teacher1.age)
成员方法的定义和访问
定义方法
class Teacher {
var name: String = _
var age: Int = _
/**
* 课程上课方法
*
* @param course 课程
*/
def teach(course: String): Unit = {
println(s"开始上[${course}]课!")
}
}
访问方法
val teacher = new Teacher()
teacher.teach("语文")
访问修饰符
① Scala 也可以通过访问修饰符,来控制成员变量和成员方法是否可以被访问
② 在 Scala 中,没有 public
关键字,任何没有被标为 private
或 protected
的成员都是公共的
// 第一种
class Teacher {
private var name: String = _
private var age: Int = _
def getName: String = name
def setName(name: String): Unit = this.name = name
def getAge: Int = age
def setAge(age: Int): Unit = this.age = age
}
// 第二种
private var name: String = _
private var age: Int = _
def getName() = this.name
def setName(name: String) = this.name = name
def getAge() = this.age
def setAge(age: Int) = this.age = age
类的构造器
① 主构造器
② 辅助构造器
主构造器
① 构造器的参数列表是直接定义在类名后面,添加了 val/var
表示直接通过主构造器定义成员变量
② 构造器参数列表可以指定默认值
③ 创建实例,调用构造器可以指定字段进行初始化
④ 整个 class
中除了字段定义和方法定义的代码都是构造代码
// 语法
class 类名(var/val 参数名:类型 = 默认值, var/val 参数名:类型 = 默认值){
// 构造代码块
}
// 示例
class Student(var name: String = "", var age: Int = 0) {
println(s"姓名:${name},年龄:${age}")
}
val student = new Student("Kyle", 23)
println(student.name)
println(student.age)
辅助构造器
① 允许通过多种方式,来创建对象,可以定义其他更多的构造器
② 除了主构造器之外的构造器称为辅助构造器
③ 辅助构造器必须要调用主构造器或者其他辅助构造器
// 格式
def this(参数名:类型, 参数名:类型) {
// 第一行需要调用主构造器或者其他构造器
// 构造器代码
}
// 示例
class Student(var name: String = "", var gender: String = "") {
println(s"姓名:${name},性别:${gender}")
def this(arr: Array[String]) = {
// 辅助构造器必须要调用主构造器或者其他辅助构造器
this(arr(0), arr(1))
}
}
// 调用
val student = new Student(Array("Kyle","male"))
println(student.name)
println(student.gender)
单例对象
单例对象表示全局仅有一个对象(类似于Java static概念),Scala 中没有 Java 中的静态成员,我们想要定义类似于 Java 的 static 变量、static 方法,就要使用到 scala 中的单例对象——object
定义单例对象
① 定义单例对象和定义类很像,就是把 class 换成 object
② 在 object 中定义的成员变量类似于 Java 的静态变量
③ 可以使用 object 直接引用成员变量
object Person {
val AREA = "China"
def main(args: Array[String]): Unit = {
println(Person.AREA)
}
}
定义成员方法
// 定义一个日期工具类
object DateUtil {
def getDateString(): String ={
java.time.LocalDate.now().toString
}
}
// 调用
DateUtil.getDateString()
程序入口
Scala 中没有静态方法,在 Scala 中,这个 main 方法必须放在一个单例对象中
① main 方法
object Demo {
def main(args: Array[String]): Unit = {
println(DateUtil.getDateString())]
}
}
② 实现 App Trait
来定义入口
object Demo extends App {
println(DateUtil.getDateString())
}
伴生对象
由于 Scala 中没有静态成员变量、静态成员方法等,通过伴生对象就可以实现和 java 一样在一个类中即有实例成员又有静态成员
一个 class
和 object
具有同样的名字,这个 object
称为伴生对象,这个 class
称为伴生类
定义伴生对象
① 伴生对象必须要和伴生类一样的名字
② 伴生对象和伴生类在同一个 scala
源文件中
③ 伴生对象和伴生类可以互相访问 private
属性
object AssociatedDemo extends App {
class Worker {
def work(): Unit = {
println(s"机器人${WORKER_NAME}开始工作!")
}
}
object Worker {
val WORKER_NAME = "Jack_01"
}
new Worker().work()
}
private[this] 访问权限
如果某个成员的权限设置为 private[this]
,表示只能在当前类中访问,伴生对象也不可以访问
// 示例:该代码编译报错,因为伴生对象不能访问伴生类中 权限为 private[this] 的成员,去掉 [this] 即可
object AssociatedDemo extends App {
class Worker(private[this] var workId: String) {
def work(): Unit = {
println(s"机器人${WORKER_NAME}开始工作!")
}
}
object Worker {
val WORKER_NAME = "Jack_01"
def printWorkId(worker: Worker): Unit = {
println(worker.workId)
}
}
val worker1 = new Worker("001")
printWorkId(worker1)
}
apply方法
apply 方法使创建对象变得更容易,不需要写 new
// apply 方法定义格式
object 伴生对象名 {
def apply(参数名:参数类型, 参数名:参数类型...) = new 类(...)
}
// 示例
object AssociatedDemo extends App {
class Worker(var workId: String, var workDay: Int) {
}
object Worker {
def apply(workId: String, workDay: Int) = new Worker(workId, workDay)
}
// 创建对象
val worker1 = Worker("001", 20)
}
继承
继承的定义
① Scala 和 Java 一样,使用 extends 关键字来实现继承
② 可以在子类中定义父类中没有的字段和方法,或者重写父类的方法
③ 类和单例对象都可以从某个父类继承
class/object 子类 extends 父类 {
..
}
类继承
class Person {
var name = ""
def getName = this.name
}
class Student extends Person
// main 方法
def main(args: Array[String]): Unit = {
val p1 = new Person()
val p2 = new Student()
p2.name = "Kyle"
println(p2.getName)
}
单例对象继承
class Person {
var name = ""
def getName = this.name
}
object Student extends Person
// main 方法
def main(args: Array[String]): Unit = {
println(Student.getName)
}
override 和 super
① 子类要覆盖父类中的一个方法,必须要使用 override
关键字
② 使用 override
来重写一个 val
字段
③ 使用 super
关键字来访问父类的成员
class Person {
val name = "Preson"
def getName = name
}
class Student extends Person {
// 重写val字段
override val name: String = "Student"
// 重写getName方法
override def getName: String = "hello, " + super.getName
}
// main 方法
def main(args: Array[String]): Unit = {
println(new Student().getName)
}
类型转换及判断
isInstanceOf / asInstanceOf
① isInstanceOf 判断对象是否为指定类的对象
// 判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
// 示例
class Person
class Student extends Person
val stu1:Person = new Student
println(stu1.isInstanceOf[Student]) // true
println(stu1.isInstanceOf[Person]) // true
② asInstanceOf 将对象转换为指定类型
// 将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
// 示例
val stu1:Person = new Student
if(stu1.isInstanceOf[Student]) {
// 类型转换
val stu2 = stu1.asInstanceOf[Student]
}
etClass 和 classOf
isInstanceOf 只能判断对象是否为 指定类以及其子类
的对象,而不能精确的判断出,对象就是指定类的对象
如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf
① getClass 可以精确获取对象的类型
② classOf[x ]可以精确获取类型
class Person
class Student extends Person
val p:Person = new Student
println(p.getClass == classOf[Student]) // true
println(p.getClass == classOf[Person]) // false
抽象类
抽象类的定义
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类,不完整定义有两种情况
① 方法没有方法体(抽象方法)
② 变量没有初始化(抽象字段)
// 格式
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象字段
val 抽象字段名:类型
// 定义抽象方法
def 方法名(参数:参数类型,参数:参数类型...):返回类型
}
abstract class Factory {
val name: String
def pipeline(pipe:String,workNo:String):String
}
抽象方法
定义抽象类,定义多个继承类,抽象方法为计算面积,按照不同的计算方式重写计算面积的方法
// 抽象类
abstract class Shape {
def area:Double
}
// 创建正方形类
class Square(var edge:Double) extends Shape {
// 实现父类计算面积的方法
override def area: Double = edge * edge
}
// 创建圆形类
class Cirle(var radius:Double) extends Shape {
override def area: Double = Math.PI * radius * radius
}
// 计算
val s1:Shape = new Square(2)
val s3:Shape = new Cirle(2)
println(s1.area)
println(s2.area)
抽象字段
abstract class Person {
val JOB :String
}
class Teacher extends Person {
override val JOB: String = "教师"
}
class Student extends Person {
override val JOB: String = "学生"
}
val p1 = new Teacher
val p2 = new Student
println(p1.JOB)
println(p2.JOB)
匿名内部类
匿名内部类是没有名称的子类,直接用来创建实例对象
// 格式
val/var 变量名 = new 类/抽象类 {
// 重写方法
}
abstract class Teacher {
def teach:Unit
}
def main(args: Array[String]): Unit = {
// 直接用new来创建一个匿名内部类对象
val teacher = new Teacher {
override def teach: Unit = println("开始上语文课")
}
teacher.teach
}
特质(trait) – 接口
特质的定义
① 特质是 Scala 中代码复用的基础单元
② 它可以将方法和字段定义封装起来,然后添加到类中
③ 与类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质
④ 特质的定义和抽象类的定义很像,但它是使用 trait
关键字
⑤ 继承多个 trait
,则使用 with
关键字
// 定义特质
trait 名称 {
// 抽象字段
// 抽象方法
// 具体字段
// 具体方法
}
// 继承特质
class 类 extends 特质1 with 特质2 {
// 字段实现
// 方法实现
}
特质作为接口使用
① class 继承单个特质
trait Logger {
// 抽象方法
def log(message:String)
}
class ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制台日志:" + message)
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("这是一条日志")
}
② class 继承多个特质
trait MessageSender {
def send(msg:String)
}
trait MessageReceive {
def receive():String
}
class MessageWorker extends MessageSender with MessageReceive {
override def send(msg: String): Unit = println(s"发送消息:${msg}")
override def receive(): String = "接收消息!"
}
③ Object 继承特质
trait Logger {
def log(message:String)
}
object ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制台消息:" + message)
}
def main(args: Array[String]): Unit = {
ConsoleLogger.log("程序退出!")
}
定义具体的方法
trait LoggerDetail {
// 在trait中定义具体方法
def log(msg:String) = println(msg)
}
class UserService extends LoggerDetail {
def add() = log("添加用户")
}
object MethodInTrait {
def main(args: Array[String]): Unit = {
val userService = new UserService
userService.add()
}
}
定义具体字段和抽象字段
① 在 trait 中既可以定义抽象字段,又可以定义具体字段
② 继承 trait 的子类自动拥有 trait 中定义的字段
③ 字段直接被添加到子类中
trait Logger{
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val TYPE :String
def log(msg:String)
}
class ConsoleLogger extends Logger{
override val TYPE: String = "info"
override def log(msg: String): Unit = {
val info = s"${TYPE} ${sdf.format(new Date)}:"+msg
println(info)
}
}
对象混入特质
Scala 中可以将 trait
混入到对象中,就是将 trait 中定义的方法、字段添加到一个对象中,给对象额外添加一些功能
val/var 对象名 = new 类 with 特质
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
def main(args: Array[String]): Unit = {
val service = new UserService with Logger
service.log("混入的方法")
}
trait 的构造机制
trait 的加载顺序
① 执行父类 A 的构造器
② 从左到右依次执行 trait 的构造器
③ 如果 trait
有父 trait
,先构造父 trait
,如果多个 trait
有同样的父 trait
,则只初始化一次
④ 执行子类构造器
class A extends B with C with D {
// 字段实现
// 方法实现
}
trait Logger {
println("执行Logger构造器")
}
trait MyLogger extends Logger {
println("执行MyLogger构造器")
}
trait TimeLogger extends Logger {
println("执行TimeLogger构造器")
}
class Person{
println("执行Person构造器")
}
class Student extends Person with TimeLogger with MyLogger {
println("执行Student构造器")
}
def main(args: Array[String]): Unit = {
new Student
}
// 执行Person构造器
// 执行Logger构造器
// 执行TimeLogger构造器
// 执行MyLogger构造器
// 执行Student构造器
trait 继承 class
trait 也可以继承 class
的,特质会将 class
中的成员都继承下来
class MyUtil {
def printMsg(msg:String) = println(msg)
}
trait Logger extends MyUtil {
def log(msg:String) = printMsg("Logger:" + msg)
}
class Person extends Logger {
def sayHello() = log("你好")
}
def main(args: Array[String]): Unit = {
val person = new Person
person.sayHello()
}
样例类
样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类
定义样例类
① 构造器的每个参数都默认成为 val
修饰(val可以省略),除非显式被声明为 var
,但是并不推荐这么做
② 自动在伴生对象中提供了 apply 方法,所以后面我们可以不使用 new
关键字就可构建对象
③ 自动提供了 unapply
方法,让我们可以通过模式匹配来获取类属性,是 Scala
中抽取器的实现和模式匹配的关键方法
④ 自动生成了toString、equals、hashCode 和 copy 方法,除非我们另外再显示给出这些方法的定义
// 语法格式
case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
// 示例
case class Student(name:String, age:Int)
def main(args: Array[String]): Unit = {
val student = Person("Kyle", 20)
println(student)
}
可变成员变量
// 可变的才能修改,不然会编译报错
case class Student(var name:String, var age:Int)
val student = Student("Kyle", 20)
student.age = 23
样例类的方法
① apply 方法
apply 方法可以让我们快速地使用类名来创建对象
② toString 方法
③ equals 方法
样例类自动实现了 equals
方法,可以直接使用 ==
比较两个样例类是否相等,即所有的成员变量是否相等
④ hashCode 方法
样例类自动实现了 hashCode
方法,如果所有成员变量的值相同,则 hash
值相同,只要有一个不一样,则 hash
值不一样
⑤ copy 方法
样例类实现了 copy
方法,可以快速创建一个相同的实例对象,可以使用带名参数指定给成员进行重新赋值
val stu1 = Student("Kyle", 20)
stu2 = stu1.copy(name = "Jack')
样例对象
使用 case object
可以创建样例对象。样例对象是单例的,而且它没有主构造器
// 语法格式
case object 样例对象名
定义枚举
trait Sex
case object Male extends Sex
case object Female extends Sex
case class Student(name:String, sex:Sex)
def main(args: Array[String]): Unit = {
val student = Student("Kyle", Male)
println(student)
}
模式匹配
模式匹配的使用场景
① switch 语句
② 类型查询
③ 使用模式匹配快速获取数据
模式匹配的几种模式
① 简单匹配模式
② 类型匹配模式
③ 守卫
④ 样例匹配模式
简单模式匹配
// 语法格式
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量3" => 表达式3
case _ => 表达式4 // 默认配
}
注:无需使用到匹配到的变量,可以使用下划线代替
val season = "01"
val result = name match {
case "01" => "春季"
case "02" => "夏季"
case "03" => "秋季"
case "04" => "冬季"
case _ => "未匹配"
}
println(result)
匹配类型
根据不同的数据类型,来执行不同的逻辑,也可以使用 match
表达式来实现
val a:Any = "test"
val result = a match {
case _:String => "String"
case _:Int => "Int"
case _:Double => "Double"
}
println(result)
守卫
在 case
语句中添加 if
条件判断,称为 守卫
val a = 3
a match {
case _ if( a >= 0 && a <= 3) => println("[0-3]")
case _ if( a >= 4 && a <= 8) => println("[4-8]")
case _ => println("未匹配")
}
匹配样例类
Scala 可以使用模式匹配来匹配样例类,从而可以快速获取样例类中的成员数据
case class Student(name:String, age:Int)
case class Score(score:Int)
val student:Any = Student("Kyle", 20)
val score:Any = Score(98)
order1 match {
case Student(name, age) => println(s"姓名:${name} 年龄:${age}")
case Score(score) => println(s"成绩为:${score}")
case _ => println("未匹配")
}
变量声明中的模式匹配
在定义变量的时候,可以使用模式匹配快速获取数据
示例 | 获取数组中的元素
// 需求
// 生成包含 1-10 数字的数组,使用模式匹配分别获取第二个、第三个、第四个元素
val arr = (1 to 10).toArray
// 第一种方法
val Array(a,b,c,d,e,f,g,h,i,j)=arr
println(b,c,d)
// 第二种方法
val Array(_, x, y, z, _*) = arr
println(x, y, z)
示例 | 获取List中的数据
// 需求
// 生成包含 0-10 数字的列表,使用模式匹配分别获取第一个、第二个元素
val list = (1 to 10).toList
val x :: y :: tail = list
println(x, y)
提取器(Extractor)
提取器主要用来提取类中的成员变量,在模式匹配中,由于只有样例类提供了 apply
和 unapply
方法,所以可以实现成员变量的提取,为了让所有的类都可以实现成员变量的提取,因此出现了提取器
定义提取器
伴生对象中的 apply
方法,可以用类名来快速构建一个对象
伴生对象中的 unapply
方法,可以将该类的对象,拆解为一个个的元素
// 语法格式
override def unapply(stu:Student):Option[(类型1, 类型2, 类型3...)] = {
if(stu != null) {
Some((变量1, 变量2, 变量3...))
}
else {
None
}
}
实现提取器
class Student(var name:String, var age:Int)
object Student {
def apply(name:String, age:Int) = {
new Student(name, age)
}
def unapply(student:Student) = {
val tuple = (student.name, student.age)
Some(tuple)
}
}
def main(args: Array[String]): Unit = {
val zhangsan = Student("Kyle", 23)
zhangsan match {
case Student(name, age) => println(s"${name} => ${age}")
}
}
泛型
类 class 和特质 trait、方法都支持泛型,集合涉及到泛型的情况比较多
定义一个泛型方法
// 语法格式
def 方法名[泛型名称](..) = {
//...
}
// 获取集合中的最后一个元素
def getLast(arr:Array[Int]) = arr(arr.length)
def main(args: Array[String]): Unit = {
println(getLast(Array(1,2,3,4,5)))
}
定义一个泛型类
注意:范型类的多个成员变量的类型可以不一致
// 语法格式
// 注意:成员变量的类型可以不一致
class 类[T](val 变量名: T)
case class Pipe[T](var a:T, var b:T)
def main(args: Array[String]): Unit = {
Pipe("step1","start")
Pipe(1,0.2)
Pipe("step2", Some(2.0))
}
上下界(上界)
// 定义上界
// 使用 <: 类型名 表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承
class 类[T<: 类型](val 变量名: T)
class Person
class Student extends Person
def demo[T <: Person](a:Array[T]) = println(a)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Student))
// 运行出错,必须是Person的子类
// demo(Array("Kyle"))
}
上下界(下界)
// 定义下界
// 下界是必须是某个类的父类(或本身)
class Person
class Student extends Person
class Gardener extends Teacher
def demo[T >: Student](array:Array[T]) = println(array)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Student))
// 编译出错:Gardener是Teacher的子类
// demo(Array(new Gardener))
}