环境统一:
jdk:1.8
安装scala SDK(针对scala语言的编译器):2.11.8
安装IDEA插件:2019.1.7
目录标题
scala简介
啥是scala:说白了就是运行在JVM上的python,可以跟java无缝衔接
scala安装配置
scala基础语法格式
1.开启scala解释器,并执行hello world
使用scala解释器可以方便我们做一些小的测试,练习
键盘windows按键+r
输入scala回车
println("hello,world")
推出scala解释器
:quit
2.变量
(1)语法格式
在scala中,使用val或者var来定义变量
var/val 变量标识:变量类型(可以省略不写) = 初始值
-
var定义是可重新赋值的变量
-
val定义的是不可重新赋值的变量
-
示例演示:
val name:String = "tom"
var name:String = "tom"
name = "jim" //重新赋值
(2)惰性赋值
当有一些变量的数据比较大时,但是不需要要马上加载到JVM内存,可以使用惰性赋值来提高效率
使用场景:当我们在大数据开发时候会编写非常赋值的sql宇哥,这些语句可能成百上前行,如果把这些sql直接加载到JVM当中会产生很大的内存开销,我们这个时候就可以使用scala的惰性赋值
- 语法格式
lazy val/var 变量名 = 表达式
4.字符串
(1)插值表达式
直接在字符串内引用引用内容写在{ }中,直接避免使用+号进行拼接
- 语法
val/var 变量名 = s"${变量/表达式}字符串"
-示例
val name = "zhangsan"
val age = 30
val sex = "male"
//使用插值表达式
val info = s"name=${name},age=${age},sex=${sex}"
(2)使用三引号
使用场景:面对一大段的文本数据需要保存,可以使用三引号
5.条件表达式
(1)有返回值的if
在scala中,if条件表达式是有返回值的
val sex ="male"
val result = if(sex="male") 1 else 0
(2)块表达式
- 在scala中使用{ }表示一个块表达式
- 和if表达式一样,块表达式也是有值得
- 值就是最后一个表达式的值
6.循环
(1)for表达式
- 语法
for(i<- 表达式/数字/集合>){
//表达式
}
- 示例演示
使用for表达式打印1-10的数字
for(i <- 1 to 10) println(i)
(2)嵌套循环
使用for表达式,打印以下字符
*****
*****
*****
*****
for(i <- 1 to 3;j<-1 to 5) {print("*");if(j==5) println("")}
(3)在for表达式中添加判读语句
for (i <- 表达式/集合/数字 if 表达式){
//表达式
}
- 示例演示
使用for表达式打印1-10直接能够整除3的数字
for(i <- 1 to 10 if i%3 == 0) println(i)
(4)for推导式
//for推导式:for表达式中以yield开始,该for表达式会构建出一个集合
val v =for(i <- 1 to 10) yield i*10
(5)while循环
var i =1
while(i <=10){
println(i)
i = i+1
}
(6)实现break
用法
-
导入Breaks包import scala.util.control.Breaks._
-
使用breakable将for表达式包起来
-
for表达式中需要退出循环的地方,添加break()方法调用
-
示例演示
使用for表达式打印1-100的数字,如果实到达50,退出for表达式
//导入scala.util.control包下的Break
improt scala.util.control.Breaks._
breakable{
for(i<- 1 to 100)
if(i>=50) break()
else println(i)
}
(7)continue跳过
实现break是用breakable{}将整个for表达式包起来,而实现continue是用breakable{}将for表达式的循环体包含起来就可以了
遇见能整除10的就不打印
import scala.util.control.Breaks._
for(i <- 1 to 100){
breakable{
if(i%10 == 0) break()
else println(i)
}
}
(8)foreach
打印1到10的数组
(1 to 10).foreach(println(_))
7.方法
(1)方法的定义
方法有两部分组成=等号左边是参数,=等号右边是方法体
def format(date:Date) = simpleDateFormat.format(date)
(2)方法的调用
- 后缀调用法
对象名.方法名(参数)
Math.abs(-1)
- 中缀调用法
对象名 方法名 参数(如果参数有多个可以使用括号括起来)
Math abs -1
- 花括号调用
Math.abs{
//表达式1
//表达式2
}
注意:只能是方法仅有一个参数时,才可以使用花括号调用法
- 无括号调用法
如果方法没有参数,可以省略方法名后面的括号
def m3()=print("hello")
m3()
8.函数
(1)定义函数
语法
val 函数变量名 = (参数名:参数类型,参数名:参数类型...)=>函数体
函数是一个变量
函数类似方法也有输入参数和返回值
无需指定返回值类型
- 示例演示
val add = (x:int,y:int) => x + y
add(1,2)
(2)方法和函数的区别
- 函数用的=> 而方法是=
- 方法是一个类下面的一个功能,例如:Math类的abs求绝对值方法
- 函数是一个变量,他可以在赋值给另一个变量
- 函数对象有apply,curried,toString,tuoled这些方法.而方法则没有
(3)方法转化为函数
- 有时候需要将方法转换为函数,作为变量传递,就需要将方法转换为函数
- 使用 _ 即可将方法转换为函数
- 示例演示
def add(x:Int,y:Int) = x+y
val a = add _
9.数组
(1)定长数组
- 数组长度步允许改变
- 数组的元素是可以改变的
//通过指定长度定义数组
val/var 变量名 = new Array[元素类型](数组长度)
//用元素直接初始化数组
val/var 变量名 = Array(元素1,元素2,元素3...)
- 示例演示
val a = new Array[Int](100)
a(0) = 100
println(a(0))
(2)变长数组
(3)遍历数组
- 使用for表达式直接遍历数组中的元素
val a = new Array[Int](1,2,3,4,5)
for(i <- a) println(i)
- 使用索引下标遍历数组中的元素
val a = new Array[Int](1,2,3,4,5)
for(i <- 0 to a.length-1) println(a(i))
数组常用算法
- 求和sum方法
val a = Array(1,2,3,4)
a.sum
- 求最大值max方法
val a = Array(1,2,3,4)
a.max
- 求最小值min方法
val a = Array(1,2,3,4)
a.min
- 排序sorted升序方法,而reverse方法,可以将数组进行反转,从而实现降序排序
val a = Array(1,2,3,4,10)
//升序
a.sorted
//降序
a.sorted.reverse
10.元组
元组可以用来包含一组不同类型的值
(1)定义元组
- 使用括号定义
val/var 元组 = (元素1,元素2,元素3...)
val a = (1,"zhangsan",20,"beijing")
- 使用箭头定义,元组内仅仅只能有两个元素
val/var 元组 = 元素1->元素2
val a = "zhangsan" -> 30
(2)访问元组
使用_1,_2,_3来访问元组中的元素
val a = "zhangsan" -> 20
a._1
11.列表
(1)不可变列表
不可变列表就是列表元素,长度都是不可改变的
var/val 变量名 = List(元素1,元素2,元素3...)
使用Nil创建
val/var 变量名 = Nil
使用::方法创建一个不可变列表
val/var 变量名 = 元素1::元素2::Nil
(2)可变列表
可变列表就是列表的元素和列表的长度是可以改变的说白了就是:列表可以实现增删改的功能
- 注意事项:
可变集合都在mutable包中(需要手动导入)
不可变集合都在immutable包中(默认导入的) - 方式1:
import scala.collection.mutable.ListBuffer
val/var 变量名 = ListBuffer[Int]()
- 方式2:
import scala.collection.mutable.ListBuffer
var/val 变量名 = ListBuffer(元素1,元素2,元素3...)
(3)可变列表操作
- 获取元素(使用括号索引值访问)
- 添加元素(+=)
- 追加元素(++=)
- 更改元素(使用括号获取元素,然后进行赋值)
- 删除元素(-=)
- 转化成不可变列表(toList)
- 转化为Array(toArray)
(4)列表常用操作
- 判断是否为空(isEmpty)
- 拼接两个列表(++)
- 获取列表的首个元素(head)和剩余部分(tail)
- 反转列表(reverse)
- 获取前缀(take(前几个元素)),获取后缀(drop(后几个元素))
- 扁平化(flaten)
扁平化也也叫压平操作,是指一个列表内还嵌套这多个列表,我们对其进行扁平化操作后,内嵌的多个列表就会被去除,但是内部的元素依旧会被保留下来
val a = List(List(1,2),List(3,4),List(5,6))
a.flatten
- 拉链(zip)和拉开(unzip)
拉链:使用zip将两个列表,组合成为一个元素为元组的列表
拉开:将一个包含元组的列表,解开成包含两个列表的元组
刚好和拉链操作是相反的 - 转换成字符串(toString)
- 生成字符串(mkString)
mkString方法可以将元素以指定的分隔符进行拼接
val a = List(1,2,3,4)
a.mkString(":")
- 并集(union)
使用union是不去重的
使用distinct操作,去除重复的元素 - 交集(intersect)
- 差集(diff)
例如a1.diff(a2),表示获取a1在a2中不存在的元素
12.集
(1)不可变集
(2)可变集
与不可变集创建方法一致,但是需要手动导入一个不可变集类
import scala.collection.mutable.Set
val a = Set(1,2,3,4)
a+=5
a-=1
13.映射
(1)不可变map
不可变map指的是元素的k,v都是不可以被改变的,也不可以新添加,删除k,v对
- 方式1:
val/var map = Map(键->值,键->值,..)
- 方式2:
val/var map Map((键,值),(键,值),...)
可以通过k值来获取v值
(2)可变map
可变map是指其中元素的k,v键值对都是可以修改的
import scala.collection.mutable.Map
val map = Map("zhangsan"->30,"lisi"->40)
map("zhangsan")=20
14.迭代器
scala针对每一类集合都提供了一个迭代器用来访问集合
使用迭代器遍历集合
- 使用iterator方法可以从集合获取一个迭代器
- 迭代器的两个基本操作
- hasNext:查询容器中是否有下一个元素
- next:返回迭代器的下一个元素
val a = List(1,2,3,4,5)
val ite = a.iterator
while(ite.hasNext)
println(ite.next)
val a = List(1,2,3,4,5)
for(i <- a) println(i)
15.函数式编程
- 基础符号讲解
=>b箭头右边b是方法体,方法体的结果就是返回值
a=>箭头左边a是传入参数
参数在函数体中出现了一次,函数体中没有嵌套调用的,就可以用_来替代函数参数
k -> v 单箭头的kv对映射
(1)遍历(foreach)
val a = List(1,2,3,4)
a.foreach(x=>println(x))
(2)映射(map)
map方法接收一个函数,将这个函数用于带每一个元素,返回一个新的列表
val lst = List(1,2,3,4)
lst.map(lst=>lst+1)
val a = List(1,2,3,4)
a.map(_+1)
(3)映射扁平化(flatmap)
切割字符串将一个文件列表转换成为一个一个的单词的列表
flatmap是先进行map,列表分为多个字符串,得到多个这种数据[“hadoop spark hadoop”],在进行flat对字符串内数据在进行拆解按照指定分隔符拆分,我们就得到最后的[“hadoop”,“spark”,“hadoop”]
[hadoop,sqoop,hadoop]
LineList = ["hadoop spark hadoop"]
lineList.flatMap(_.split(" "))
返回的是
[“hadoop”,“spark”,“hadoop”]
(4)过滤(filter)
过滤符合一定条件的元素
val lst = List(1,2,3,4,5,6)
lst.filter(lst => lst % 2)
val lst = List(1,2,3,4,5,6)
lst.filter(_ % 2)
(5)是否存在(exists)
在这里插入代码片
(6)排序(sorted,sortBy,sortWith)
1.默认升序排序sorted
List(3,1,2,9,7).sorted
2.指定字段排序sortBy
val lst = List("01 hadoop","02 flume","03 hive","04 spark")
lst.sortBy(_.split(" ")(1))
3.自定义排序
根据一个函数来进行自定义排序
val a = List(2,3,1,6,4,5)
a.sortWith((x,y) => if(x<y)true else false)
(7)分组(groupBy)
val a = List("张三"->"男","李四"->"女","王五"->"男") //列表内放置的一对对元组
val groupMap = a.groupBy(x=>x._2) //元组的访问使用_1,_2...来访问,这里的下滑线_不是代替参数的作用
groupMap.map(x=>x._1 -> x.2.size)
(8)聚合计算(reduce)
可以将一个列表中的数据合并为一个
x是上一次两个数相加的结果值,y是下一个元素的值
val a = List(1,2,3,4,5,6,7,8,9,10)
a.reduce((x,y)=>x+y)
val a = List(1,2,3,4,5,6,7,8,9,10)
a.reduce(_+_)
(9)折叠(fold)
val a = List(1,2,3,4,5,6,7,8,9,10)
a.fold(0)(_+_) //第一个括号是指定聚合前的初始值
scala类和对象
1.类和对象
(1)创建类和对象
- 使用class来定义一个类
- 使用new来创建对象
创建一个单例对象,如果要有main方法就必须创建一个单例对象,此对象好比是程序的入口
object ClassObject {
class Person{ //创建一个类
}
def main(args: Array[String]): Unit = {
val person = new Person() //创建对象,idea快捷键是new Person().var
println(person)
}
(2)简写方式
- 如果类是空的,没有任何成员可以省略{ }
- 如果构造器的参数为空,可以省略()
object ClassObject {
class Person
def main(args: Array[String]): Unit = {
val person = new Person
println(person)
}
}
(3)定义和访问成员变量
- 在类中使用var/val来定义成员变量
- 对象直接使用成员变量来访问成员变量
在这里插入代码片
(4)使用下划线来初始化成员变量
object ClassObject {
class Person {
//定义成员变量
var name: String = _
var age: Int = _
}
def main(args: Array[String]): Unit = {
val person = new Person
person.name = "张三"
person.age = 30
println(person.name,person.age)
}
}
(5)定义成员方法
类可以有自己的行为,scala中也可以通过定义成员方法来定义类的行为
object ClassObject {
class Customer {
//定义成员变量
var name: String = _
var sex: String = _
//定义成员方法
def printHello(msg:String): Unit =println(msg)
}
def main(args: Array[String]): Unit = {
val customer = new Customer
customer.printHello("你好")
}
}
(6)访问修饰符
通过访问修饰符private,来控制成员变量和成员方法是否可以被访问
object ClassObject {
class Person{
private var name = ""
private var age = 0
def getName() = this.name
def getAge() = this.age
def setName(name:String) = this.name = name
def setAge(age:Int) = this.age = age
//获取姓名和年龄返回的是一个元组
private def getNameAndAge() = (this.name,this.age)
}
def main(args: Array[String]): Unit = {
val person = new Person
person.setName("张三")
person.setAge(30)
}
}
(7)类的构造器(java的无参构造有参构造)
当创建类对象的时候,会自动调用类的构造器,主构造器的参数会自动成为成员变量
1.主构造器
object ClassObject {
class Person(var name:String="",var age:Int=0){
println("调用主构造器")
}
def main(args: Array[String]): Unit = {
val zhangsan = new Person("张三",20)
println(zhangsan.name)
println(zhangsan.age)
}
}
(2)辅助构造器
- 辅助构造器的使用场景:可以实现主构造器,能允许多种方式来创建对象
- 定义辅助构造器与定义方法一样,也使用def关键字来定义
- 这个方法的名字为this
- 辅助构造器是定义在主构造器内容部的
def this(参数名:类型,..){
//第一行需要调用主构造器,或者其他构造器
//构造器代码
}
通过辅助构造器实现创建对象时数组传递构造参数
object ClassObject {
class Customer(var name:String="",var addr:String=""){
def this(data:Array[String]){
this(data(0),data(1)) //this是主构造器自己,传入数组的0号位,和1号位参赛
}
}
def main(args: Array[String]): Unit = {
val customer = new Customer(Array("张三","北京"))
println(customer.name)
println(customer.addr)
}
}
2.单例对象–object
(1)单例对象
单例对象在java中就是static静态成员变量.就是把所修饰的内容提高到类全局中,表示整个类共享此内容
例如:学生类有不同的学生,小明,小王,小李,但是都是同一所学校,北京蓝天幼稚园,我们通过static 蓝天幼稚园.可以把此变量提高到类全局,凡是本类的都会共享此数据.
- 示例1
object ClassObject {
//创建单例对象
object Dog{
//定义了一个单例对象的成员.类似java的static
val LEG_NUM = 4
}
//访问单例对象的成员变量
def main(args: Array[String]): Unit = {
Dog.LEG_NUM
}
}
- 示例2
object ClassObject {
//创建一个单例对象,定义成员方法
object PrintUtil{
def printSpliter() = {
println("-" * 15)
}
}
//访问单例对象的成员变量
def main(args: Array[String]): Unit = {
PrintUtil.printSpliter()
}
}
(2)工具类案例
需求
- 编写一个DateUtil工具类专门用来格式化日期时间
- 定义一个方法,用于将日期转化为年月日字符串,例如:2030-10-05
import java.text.SimpleDateFormat
import java.util.Date
object ClassObject {
object DateUtil {
//scala可以直接的调用java的相关包
private val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd")
//定义一个用于日期格式化的方法
def format(date:Date)= simpleDateFormat.format(date)
}
//访问单例对象的成员变量
def main(args: Array[String]): Unit = {
val now = new Date()
println(DateUtil.format(now))
}
}
(3)main方法
如果要运行一个程序,必须要有一个main方法.,而这个main方法必须放在一个单例对象中
object ClassObject {
def main(args: Array[String]): Unit = {
println("hello scala")
}
实现AppTrait来定义入口
创建一个object,继承自App,然后将需要编写在main方法中的代码,写在object的构造方法体内
object 单例对象名 extends App {
//方法体
}
object ClassObject extends App{
println("hello Scala")
}
3.伴生对象
使用场景是:
1. 经常会有一些类,同时又实例成员又有静态成员,我们要实现这样的效果,就需要使用伴生对象来实现
2. 还可以使用伴生对象来实现快速创建对象
3. 弥补类中不能定义 static 属性(单例对象)的缺陷。
(1)定义伴生对象
- 定义一个 class 并在这里面实现所有的实例成员。
- 添加一个 object ,这个 object 要与上面的 class 同名,然后在这里面实现所有的静态成员。
注意:
- 伴生对象object+名称,必须要和伴生类class+名称,名称一致
- 伴生对象和伴生类在同一个scala源文件中
- 伴生对象和伴生类可以互相访问private属性
object ClassObject{
//1.创建一个伴生类
class CustomerService{
def save()= println(CustomerService.SERVICE_NAME)
}
//2.创建一个伴生对象
object CustomerService{
private val SERVICE_NAME = "保存客户"
}
//3.创建对象,调用方法
def main(args: Array[String]): Unit = {
val service = new CustomerService()
service.save()
}
}
(2)private[this]访问权限
如果某个成员的权限设置为private[this],表示只能在当前类中访问.伴生对象也不可以访问
object ClassObject{
class Persion(private[this] var name:String)
//创建Person类伴生对象
object Persion{
def printPersion(p:Persion): Unit ={
println(p.name) //报错
}
}
}
4.继承
使用场景:通过集成来减少重复的代码
object ClassObject{
class Person{
var name:String = _
def getName() = this.name
}
//2.创建一个Student类,继承Person类
class Student extends Person
//3.创建Student对象访问其成员变量
private val student = new Student
student.name
}
5.override,super
使用override重写父类成员,可以使用super来引用父类
- 子类要覆盖重写父类的一个方法,必须要使用override关键字
- 使用override来重写一个vai字段
- 使用super关键字来访问父类的成员方法
- 覆盖重写成员字段必须是不可变val类型
object ClassObject{
class Person{
val name = ""
def getName() = this.name
}
class Student extends Person {
override val name = "张三"
override def getName(): String = "hello"+name
}
def main(args: Array[String]): Unit = {
val student = new Student
println(student.getName())
}
}
6.isInstanceOf/asInstanceOf类型判断和类型转换
- isInstanceOf判断对象是否为指定类对象或其子类
- asinstanceOf将对象转换为指定类型,等同于一个强转
- 用法
//判断对象是否为指定类型
var trueorFalse = 对象.inInstanceOf[类型]
//将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
object ClassObject{
class Person
class Student extends Person
def main(args: Array[String]): Unit = {
val student = new Student
if(student.isInstanceOf[Student]){ //这个中括号[数据类型]不可省略
println(student.asInstanceOf[Student]) //这个中括号[数据类型]不可省略
}
}
}
7.getClass和classOf
使用场景:使用isInstance只能去判断一个对象是否为指定类对象或其子类(不精准,父类子类他都会说是true),而不能精准的获得说这个对象的具体类是子类还是父类
当使用getClass他会精准的区分其类的类型是子类类型,还是父类类型,还是什么其他类型.不会因为子父类一继承,而把它们归为一谈
- p.getClass可以精准的获取对象的类型
- classOf[x]可以精准的获取类型
- 使用==操作符可以直接比较类型
object ClassObject{
class Person
class Student extends Person
//创建对象,判断对象的类型(isInstanceOf)
def main(args: Array[String]): Unit = {
val student:Person = new Student
if(student.isInstanceOf[Person]){
println("isinstance判断:是Person类型")
}
else {
println("isinstance判断:不是Person类型")
}
//使用getClass获取对象类型
//使用classOf获取类的类型
if (student.getClass == classOf[Person]){
println("getClass判断是Person类型")
}
println("getClass判断不是Person类型")
if (student.getClass == classOf[Student]){
println("getClass判断是student类型")
}
else {
println("不是student类型")
}
}
}
8.抽象类
- 抽象方法:方法没有方法体
- 抽象字段:变量没有初始值
- 抽象类与java定义抽象类一致.在class关键字前面加上abstract关键字
package com.yuge.scala.experence
object ClassObject{
abstract class Shape{
//定义抽象方法,返回值是Double
def area():Double
}
class Square(var edge:Double) extends Shape{
//计算正方形面积
override def area(): Double = edge*edge
}
//计算长方形面积
class Rectangle(var length:Double,var width:Double) extends Shape{
override def area(): Double = length * width
}
//计算圆形面积
class Circle(var r:Double) extends Shape{
override def area(): Double = Math.PI * r * r
}
//创建实现类对象,求面积
def main(args: Array[String]): Unit = {
//求长方形面积
val square = new Square(2.0)
println(square.area())
}
}
9.匿名内部类
scala匿名内部类与java一致:没有名称的子类
使用场景:直接在程序中创建,实现抽象类,其主要作用就是可以简单便捷的实现抽象类,减少在创建一个单独类实现抽象方法,在调用抽象方法的过程
object ClassObject{
//定义一个抽象方法
abstract class Person{
def sayHello()
}
def main(args: Array[String]): Unit = {
new Person {
override def sayHello(): Unit = println("hello")
}.sayHello()
}
}
10.特质(java的接口)
- 他可以将方法和字段定义封装起来,然后添加到类中
- 一个类只能继承一个父类,而一个类可以添加任意数量的特质
- 定义使用的关键字是trait
(1)定义特质
- 定义
trait 名称{
//抽象字段
//抽象方法
}
- 特质的继承
class 类 extends 特质1 with 特质2{
//字段实现
//方法实现
}
- 示例1
object ClassObject{
//1.创建一个特质
trait Logger{
//可以在特质中定义抽象方法
def log(msg:String)
}
//2.创建一个实现类,继承特质
class ConsoleLogger extends Logger{
override def log(msg: String): Unit = println("控制台消息"+msg)
}
//3.创建实现类的对象,调用实现方法
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("NullPointer Exception")
}
}
- 示例2:多继承特质
object ClassObject{
//1.创建两个个特质,两个抽象方法
trait MessageSender{
def send(msg:String)
}
trait MessageReceive{
def receive():String
}
//2.创建一个实现类,继承特质
class MessageWorker extends MessageReceive with MessageSender{
override def receive(): String = "接收成功"
override def send(msg:String): Unit = println("控制台发送"+msg)
}
//3.创建实现类的对象,调用实现方法
def main(args: Array[String]): Unit = {
val worker = new MessageWorker
worker send("收到了么?")
println(worker.receive())
}
}
- 示例3:object单例对象也可以继承特质
package com.yuge.scala.experence
object ClassObject{
//1.创建一个特质,定义抽象方法
trait Logger{
def Log(msg:String)
}
//2.定义单例对象,继承特质
object ConsoleLogger extends Logger{
override def Log(msg:String): Unit = println("控制台消息"+msg)
}
//3调用单例对象的实现方法
def main(args: Array[String]): Unit = {
ConsoleLogger.Log("hello Scala")
}
}
(2)特质内部定义具体方法
使用场景:主要是解决出现大量的重复代码,我们可以把它们封装在特质中,在使用时直接继承特质然后后调用,避免出现反复编写重复代码
object ClassObject{
//1.创建一个特质,定义具体实现方法
trait Logger{
def Log(msg:String) = println(msg)
}
//2.定义一个类,继承特质
class UserService extends Logger{
def add() = Log("添加用户")
}
//3调用单例对象的实现方法
def main(args: Array[String]): Unit = {
val service = new UserService
service.add()
}
}
(3)使用trait实现模板模式
需要分析:
- 编写一个日志输出工具,分别有info,warn,error三个级别的日志输出
- 日志输出方式要求设计为可扩展的:可以输出到数据库,控制台,文件
object ClassObject{
//1.创建一个特质,具体方法会调用抽象方法,输出不同级别日志
trait Logger{
def Log(msg:String)
//具体方法->去实现抽象方法
def info(msg:String) = Log("信息:"+msg)
def warn(msg:String) = Log("警告:"+msg)
def error(msg:String) = Log("错误:"+msg)
}
//2.定义一个实现类,继承特质
class ConsoleLogger extends Logger{
override def Log(msg: String): Unit = println(msg) //输出到哪里,目前是控制台
}
//3调用单例对象的实现方法
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.info("aaaaa")
logger.warn("sdasfaf")
logger.error("zxsdsd")
}
}
(4)对象混入trait
可以将trait混入到对象中,就是将trait中定义的方法,字段添加到一个对象中去
使用场景:是给一个对象可以额外的添加额外的方法(这种非侵入式编写方式类似于python的装饰器)
val/var 对象名 = new 类 with 特质
object ClassObject{
//1.创建一个特质,实现方法log打印
trait Logger{
def log(msg:String) = println(msg)
}
//2.创建一个没有任何方法的类
class UserService{
}
//3调用单例对象的实现方法
def main(args: Array[String]): Unit = {
val service = new UserService with Logger //非侵入式增加对象的额外方法
service.log("hello Scala")
}
}
(5)调用链模式
package com.yuge.scala.experence
object ClassObject{
//1.定义一个HandLerTrait
trait HandlerTrait{
def handle(data:String) = {
println("处理支付数据...")
}
}
//2.定义两个trait(数据校验,签名校验)
trait dataCheck extends HandlerTrait {
override def handle(data: String): Unit = {
println("数据校验...")
super.handle(data)
}
}
trait signatureCheck extends HandlerTrait{
override def handle(data: String): Unit = {
println("签名校验..")
super.handle(data)
}
}
//3.定义一个支付服务类,基础数据校验,签名校验
/*
注意这里通过with两个特质的关系是signatureCheck调用super就会进行dataCheck
如果说是peyService虽然直接继承dataCheck内所有方法,但是如果是想要调用dataCheck进行执行,就必续通过signatureCheck的super调用,才可以实现,他们变成了一条由with串起来的链子
*/
class payService extends dataCheck with signatureCheck{
override def handle(data: String): Unit = {
println("准备支付...")
super.handle(data)
}
}
//4.创建支付服务对象,调用支付方法
def main(args: Array[String]): Unit = {
val service = new payService
service.handle("支付数据数据")
}
}
- 返回结果
准备支付…
签名校验…
数据校验…
处理支付数据…