类似 Java Switch Case 语法,但 Scala Match Case 功能更为强大,且无代码穿透问题
最重要的事情是——模式匹配本身就是个函数,并且通常会省略"x => x match { case }" 为 " " 无关键字的匿名函数形式,对于Tuple元组格式,则会简写为
“{ case(t1,t2) => … }” 如果不写明case 的话,是无法对 (t1,t2) 进行类型推断,会当成2个参数,而不是一个元组。
object Test {
def main(args: Array[String]): Unit = {
//完整写法
List(1,2,3,4).map(x=>x match {
case 4 => println("Str")
case x:Int => println("Int")
})
//简写
List(1,2,3,4).map({
case 4 => println("Array")
case x:Int => println("Int")
})
}
}
object Scala01_TestMatch {
/*0.java switch case 会出现 “代码穿透”问题,而Scala不存在,每个case会自行中断
switch(byte|short|int|char|enum(1.5)|String(1.7){case:break; ...; default:;}*/
//1.Scala作者认为,Java Switch普通版与 IfElse 无区别,但对匹配行为感兴趣,所以就进行了全面增强
def main(args: Array[String]): Unit = {
var a = 10
val b = 20
//控制变量[switch(var)]
val control = 'd'
//2. 语法:控制变量 match { case Boolean判断式(简写·值) => {代码体} ... case _ => {}}
val result = control match {
//值可以是任何类型,而不只是字面量
case ('+') => {a += 1; a+b}
case '-' => a - b
//所有case都不匹配,那么会执行case _ 分支
case _ => "illegal"
}
println(result)
}
}
可以这样理解:常量值匹配,是范围匹配的特殊形式,对范围匹配格式进行了简化
object Scala02_TestMatch {
def main(args: Array[String]): Unit = {
println(abs(-8))
}
def abs(num:Int)={
num match {
//模式守卫 ==> 范围匹配:
//case 形参 Boolean判断式(if语句) => block
case i if i >=0 => i
case j if j <0 => -j
case _ => "illegal"
}
}
}
**重点:**case 顺序!!! 满足当前case 是不会向下执行的!!! 注意相似匹配范围的 case 从范围小 ->大 顺序排列。
object Scala03_TestMatch {
def main(args: Array[String]): Unit = {
println(describe(true))
}
/**
* 字面量·值匹配
* @param x 字符串,字符,数字,布尔值等
* @return String
*/
def describe (x:Any)= x match {
case 5 => "Int 5"
case "hello" => "String hello"
case true => "Boolean True"
case '+' => "Char +"
case _ => "illegal"
}
}
特殊:泛型擦除
object Scala04_TestMatch {
def main(args: Array[String]): Unit = {
//1.非泛型类型,可识别
println(describe(1))
//1.1 Array没有泛型,是非泛型类型 Array(),只是匹配内部元素类型的时候可用Array[]而已
println(describe(Array(1,2,3,4,5)))
println(describe(Array("abc")))
//2.泛型类型,如List无法保留泛型类型,不论内部存储什么类型元素,都会被匹配到
//也称为:泛型擦除
println(describe(List("a","b","c")))
/*
Int
List[]
Array[Int]
Other Type
*/
}
def describe(x:Any)= x match {
case a:Int => "Int"
case b:String => "String"
case c:List[Double] => "List[]"
//case c:List[_] => "List[]" //匹配所有类型
case d:Array[Int] => "Array[Int]"
case _ => "Other Type"
}
}
object Scala05_TestMatch {
def main(args: Array[String]): Unit = {
for(arr <- Array(Array(0),Array(1,0),Array("hello",90),Array(0,1,0),Array(1,1,0),Array(1,1,0,1))){
/*println("result" + arr match {
case Array(0) => "0" //error:constructor cannot be instantiated required String
})*/
val result = arr match {
//匹配元素只有0的 [1.固定内容数组]
case Array(0) => "0"
//匹配有2个元素的[2.元素个数+类型不可变数组],然后将元素按次序赋给x,y
case Array(x:Int, y:Int) => x + "," + y
//匹配以0开头的[3.元素数量可变数组]
case Array(0, _*) => "以0开头的数组"
case _ => "something else"
}
println("result = " + result)
}
}
}
方式1:同Array
object Scala06_TestMatch {
def main(args: Array[String]): Unit = {
//(0)思考,如果要匹配如 List(88) 只含有一个元素的列表,并原值返回.应该怎么写
for (list <- List(List(0), List(88), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {
val result = list match {
//匹配List(0)
case List(0) => "0"
//匹配只有一个元素的List(注意,这里不能使用'_',会导致Error:unbound placeholder parameter
case List(a) => a
//匹配只有两个元素的List
case List(x, y) => x + "," + y
//匹配以0为起始元素的List集合
case List(0, _*) => "0 ..."
case _ => "something else"
/*
0
88
1,0
0 ...
something else
*/
}
println(result)
}
}
}
方式2:独有
object Scala06_TestMatch {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
//1.'::' 是 List 与 单个元素 进行 拼接的操作符
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
}//pln 1-2-List(5, 6, 7)
list match {
//2.也就是,只要出现:: 就一定是有一边为 List 对象
//3.规定:最右侧为元素个数可变的List对象,'::' 前变量均代表单个元素
//4.这样依赖,就可以对 List 进行 过滤删除选择
case first :: second :: third :: rest => println(rest)
case _ => println("something else")
}//pln List(6, 7)
}
}
基础
object Scala07_TestMatch {
def main(args: Array[String]): Unit = {
for ( tuple <- Array(
(0, 1), //0 ...
(1, 0), //1 0
(1, 1), //1 1
(1, 0, 0),//something else
(1, 0, 2)//something else
)){
tuple match {
// (0,1)先匹配 (0,_) 就不匹配 (a,b)
case (0,_) => println("0 ...")
// 只匹配数量为2 且 第二位为0的元组
case (y,0) => println(s"$y" + " 0")
// 只匹配数量为2的元组
case (a,b) => println(s"$a $b")
case _ => println("something else")
}
}
}
}
扩展
def main(args: Array[String]): Unit = {
//特殊需求1:打印元组第一个元素
//方式1 for(tuple<-arr){ tuple match { case(a,b) } }
println("--- 方式1 match case ---")
for (tuple <- Array(("a", 1), ("b", 2), ("c", 3),("d", 4, 0))) {
tuple match {
case (a, b) => println(a)
//case (a, _*) => println(a) // Case clauses excepted 元组没有匹配可变个数
case _ => println("Illegal")
}
}// plt a b c Illegal
//方式2 for(tuple<-arr){ tuple._1 } , 只适用于 只有2个元素的tuple对象 ==> 对偶元组数组,否则报错
println("--- 方式2 tuple._1 ---")
for (tuple <- Array(("a", 1), ("b", 2), ("c", 3),("d", 4 /*,0*/))) {
println(tuple._1)
}// plt a b c d
//方式3.1 for((a,b)<- arr){ a }
println("--- 方式3.1 (a,b) <- Array(,,) ---")
for ((a, b) <- Array(("a", 1), ("b", 2), ("c", 3), ("d", 4, 0))) {
println(a)
}// plt a b c
//方式3.2 for((a,_)<- arr){ a } '_' 不可用
println("--- 方式3.2 (a,_*) <- Array(,,) ---")
for ((a,_) <- Array(("a", 1), ("b", 2), ("c", 3), ("d", 4, 0))) {
println(a)
}// plt a b c
//方法3.1常量类匹配用法 for(("a",b)<- arr){ b }
println("--- 方式3.1特例 (\"a\",b) <- Array(,,) ---")
for (("a",b) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(b)
}// plt 1
//特殊需求2:给元组元素命名
var (id,name,age): (Int, String, Int) = (100, "zs", 20)
println((id,name,age))
println("--------------")
//特殊需求3:遍历集合中元组(String,count),令 count * 2
var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
//方法1 map(t => (t._1,t._2))
// ((,)=>{}) 这样(,)元组的形式不能作为元组参数进行传递,不被识别为元组对象,而是两个参数
// 而(单个对象t => (t._1,t._2)) 可以被类型推断为 元组变量
println(list.map(t => (t._1, t._2 * 2)))
//方法2 map{case (word,count)=>(word,count*2)}
// map{} 省略了 (变量 match)...
println(
list.map{
case (word,count)=>(word,count*2)
}
)
//特殊需求3.1 元组嵌套,遍历集合中元组(String,(String,count)),令 count * 2
var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
println(
list1.map{
case (groupkey,(word,count))=>(word,count*2)
}
)
}
提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]
提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]
提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
object Scala08_TestMatch {
def main(args: Array[String]): Unit = {
// new User() == User() 【apply方法】
val user: User = new User("zhangsan", 11)
val result = user match {
//case User("zhangsan", 11)将调用unapply()对象提取器,user(被匹配的user match{})作为unapply方法参数
//unapply方法将user对象的name和age属性提取出来,与User("zhangsan", 11)中的属性值进行匹配
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
class User(val name: String, val age: Int)
object User{
def apply(name: String, age: Int): User = new User(name, age)
/**
* case中对象.unapply方法(提取器)返回Some,仅当提取属性均一致,匹配成功;属性不一致,返回None匹配失败。
* @param user 被匹配的对象 user match { case User(样例对象) }
* @return 返回Some对象(包含被提取对象user的属性),如果被提取对象为null,则返回None
*/
def unapply(user: User): Option[(String, Int)] = {
if (user == null)
None
else
Some(user.name, user.age)
}
}
//样例类 对 对象模式匹配的优化
case class User(name: String, age: Int)
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
case class Person(name: String, age: Int)
object Scala09_TestMatch {
def main(args: Array[String]): Unit = {
case class Person(name: String, age: Int)
//1.元组中数据的变量声明
val (x, y) = (1, 2)
println(s"x=$x,y=$y")
//2.数组中数据的变量声明
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second")
//3.类属性的变量声明
val Person(name, age) = Person("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
def main(args: Array[String]): Unit = {
val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
//1.将Map中的一个个对偶元组,以(k,v)方式命名 而不是 tuple 整个对象
for ((k, v) <- map) {
println(k + " -> " + v) //3个
}
println("----------------------")
//1.2方法1的特殊使用,遍历value为定值的 k-v ,如果v不是0,过滤
for ((k, 0) <- map) {
println(k + " --> " + 0) // B->0
}
println("----------------------")
//1.3方法1+for循环守卫 if v == 0 是一个过滤的条件
for ((k, v) <- map if v >= 1) {
println(k + " ---> " + v) // A->1 和 c->33
}
}
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。
总结:偏函数,就是对 集合 内部分数据的 处理。
偏函数定义:
//功能:返回输入的List集合的第二个元素
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
//反编译
val second = new PartialFunction[List[Int], Option[Int]] {
//检查输入参数是否合格
override def isDefinedAt(list: List[Int]): Boolean = list match {
case x :: y :: _ => true
case _ => false
}
//执行函数逻辑
override def apply(list: List[Int]): Option[Int] = list match {
case x :: y :: _ => Some(y)
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UohZar3E-1575361305616)(i\偏函数.png)]
偏函数使用方法:
def main(args:Array[String]): Unit = {
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
//注:不能直接使用 second(List(1,2,3)),这样会直接调用apply方法,而是使用applyOrElse
second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
}
偏函数实操:
//需求:将该List(1,2,3,4,5,6,"test")中的Int类型的元素加一,并去掉字符串。
object Scala10_TestPianFunc {
def main(args: Array[String]): Unit = {
//方法1:map.filter
println(List(1, 2, 3, 4, 5, 6, "test").map {
_ match {
case i: Int => i + 1
case s: String => s
}
}.filter(a => a.isInstanceOf[Int]))
//方法2:PartitionFunction
//final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That]): That
println(List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 })
}
}
工作原理及其存在意义:
当编译器第一次编译失败时,会在当前的环境中查找能让代码编译通过的方法,如将类型进行转换,实现二次编译
实质:就是简化版的 Java 装饰设计模式,为已有且不可更改源码的类型 增添 新方法。
实质:“构造函数”,写在外面的构造函数
格式:implicit def funcName(){}
实例:
object Scala02_TestImplicit {
//隐式转换函数,函数以 implicit 关键字修饰
//作用:令编译器自动转换对象类型(前提是 构造参数 只有一个,且为目标类型)
//实质:"构造函数",写在外面的构造函数(装饰模式的精髓)
implicit def convert(a: Int) = {
new MyRichInt(a)
}
def main(args: Array[String]): Unit = {
// Int 隐式/自动转换为 MyRichInt 类型
// 当想调用对象方法时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则
println(2.myMax(6))
}
}
// 装饰类
class MyRichInt(val self: Int /*被装饰类型Int*/) {
def myMax(i: Int): Int = {
if (self < i) i else self
}
def myMin(i: Int): Int = {
if (self < i) self else i
}
}
Scala 2.10(现2.13)之后提供了隐式类,与隐式函数一样以 implicit 关键字修饰,但隐式类是对隐式函数的超扩展
规则:
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的
实例1:简单版(阐述 隐式方法与隐式类)
object Scala04_TestImplicit {
def main(args: Array[String]): Unit = {
println(2.myMax(6))
}
//implicit def convert(){} 方法被省
/**
* 隐式转换类
* @param self
*/
implicit class MyRichInt(val self: Int) {
def myMax(i: Int): Int = {
if (self < i) i else self
}
def myMin(i: Int): Int = {
if (self < i) self else i
}
}
}
实例2:复杂版(阐述,隐式类的作用范围)
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,
object TestTransform extends PersonTrait {
def main(args: Array[String]): Unit = {
//(1)首先会在当前代码作用域下查找隐式实体
val teacher = new Teacher()
teacher.eat()
teacher.say()
}
class Teacher {
def eat(): Unit = {
println("eat...")
}
}
}
trait PersonTrait { /*特质是特殊的类,也有伴生对象,且可相互访问*/ }
object PersonTrait {
// 隐式类 : 类型Teacher => 类型Person
implicit class Person(user:Teacher) {
def say(): Unit = {
println("say...")
}
}
}
普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数
规则:
1.同一个作用域中,相同类型的隐式值只能有一个
2.编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
3.隐式参数优先于默认参数,可与默认参数一起使用
4.如果隐式参数用于代替默认值则方法调用时不得写()
实例:
object Scala03_TestImplicit {
def main(args: Array[String]): Unit = {
//隐式转换参数
//作用:省略了外层嵌套函数,直接以implicit关键字来代替
//也就是:隐式转换参数 优先级高于 默认参数
implicit var name = "banhua"
sayHi
//等价于sayHH()
}
def sayHi(implicit name:String="zhangshan")={
println("hello:"+name)
}
def sayHH(na:String = "banhua") = {
sayHi(na)
}
}
1.首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象),一般是这种情况
2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找
类型的作用域:类与该类型相关联的全部伴生对象以及该类型所在包的包对象
import java.io.FileNotFoundException
object Scala01_TestException {
def main(args: Array[String]): Unit = {
//1.在Scala中,不区分受检(编译时异常)和非受检异常(运行时异常)
//throw FileNotFoundException
try{
var a:Int = 10 / 0
}catch{
//2.Scala允许任何顺序的 case e:具体类型Exception,即便无法到达
case e:ArithmeticException=>println("发生算术异常了")
case e:Exception =>println("发生异常了")
}finally {
println("finally执行了")
}
//f11()
}
//3.如果想直接抛出异常,使用 @throws(classOf[具体类型Exception]),嵌套函数不需要注明
@throws(classOf[NumberFormatException])
def f11()={
"abc".toInt
}
}
基本定义语法/泛型模板:class ClassName[T] () {}
协变定义:class ClassName[+T] () {}
逆变定义:class ClassName[-T] () {}
不可变定义:class ClassName[T] () {}
//可选填项:T,+T,-T
class Student[-T] {}
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
object Test {
def main(args: Array[String]): Unit = {
//1.[T]不可变:定义时必须 = 前后泛型相同
val stu11:Student[Child] = new Student[Child]
// Error: Require Child,but now is Parent / SubChild
//val stu12:Student[Child] = new Student[Parent]
//val stu13:Student[Child] = new Student[SubChild]
//2.[+T]协变:定义时,可向子类扩展功能+++
val stu21: Student[Child] = new Student[Child]
// Error: Require Child,but now is Parent
//val stu22: Student[Child] = new Student[Parent]
val stu23: Student[Child] = new Student[SubChild]
//3.[-T]逆变:定义时,可向父类缩小功能---
val stu31: Student[Child] = new Student[Child]
val stu32: Student[Child] = new Student[Parent]
// Error: Require Child,but now is SubChild
//val stu33: Student[Child] = new Student[SubChild]
}
}
//1.Java 上下限
class Student {}
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
public class Test {
public static void main(String[] args) {
System.out.println("--- 其及子类 extends -> c类 ---");
testS(Child.class);
testS(SubChild.class);
//Error:Cannot be applied
//testS(Parent.class);
System.out.println("--- 父类及其 super <- c类 ---");
testX(Child.class);
testX(Parent.class);
//Error:Cannot be applied
//testX(SubChild.class);
}
//其及子类 extends -> c类,上限
public static void testS(Class extends Child> c){
System.out.println(c.getName());
}
//父类及其 super <- c类,下限
public static void testX(Class super Child> c){
System.out.println(c.getName());
}
}
//2.Scala 上下限
只需要记住上限语法:子类 <: 父类(范围:子类<-及其)
class PList[T <: Person]{ //泛型上限}
class Student[T] {}
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
object Scala05_TestGeneric {
def main(args: Array[String]): Unit = {
val parent = new Parent
val child = new Child
val subChild = new SubChild
println("--- [A <: Child] 上限 子类<-及其 ---")
testS(child)
testS(subChild)
//Error:testS type parameter bounds Childs,but now is testS(Parent)
//testS(parent)
println("--- [A >: Child] 上限 父类->及其 / 无限制 ---")
testX(child)
testX(parent)
//!!!编译运行无错
testX(subChild)
println("--- [A] 无限制 ---")
test0(child)
test0(parent)
test1(subChild)
println("--- test0 == test1 二者等价,只不过是泛型名称不同 ---")
}
//上限:仅其及子类可传
def testS[A <: Child](a:A): Unit ={
println(a)
}
//下限:无限制 【换句话说,不存在下限概念】
def testX[A >: Child](a:A): Unit ={
println(a)
}
//无限制
def test0[A](a:A): Unit ={
println(a)
}
def test1[Child](a:Child): Unit ={
println(a)
}
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("--- Test.class ---");
System.out.println(TestClass.class);
System.out.println("--- Class.forName ---");
System.out.println(Class.forName("TestClass"));
System.out.println("--- t.getClass ---");
TestClass t = new TestClass();
System.out.println(t.getClass());
/*Result:
--- Test.class ---
class TestClass
--- Class.forName ---
静态初始化
class TestClass
--- t.getClass ---
非静态初始化
构造函数初始化
class TestClass
*/
}
}
public class TestClass {
public TestClass() {
System.out.println("构造函数初始化");
}
static{
System.out.println("静态初始化");
}
{
System.out.println("非静态初始化");
}
}
//1.Java 泛型集合装载
//泛型引用,被之前定义的已存在对象赋值时,泛型不起限制作用
class Parent{}
class Child extends Parent{}
class SubChild extends Child{}
public class Test {
public static void main(String[] args) {
ArrayList std = new ArrayList();
// new SubChild ; OK
std.add(new Parent());
ArrayList childs = new ArrayList<>();
childs = std;
//Java 泛型管理较Scala 宽泛,子类能装载父类,但不能以子类类型调用父类对象
//但父类泛型数组即可以装载子类对象,也可以以父类类型调用子类对象
for (Child child : childs) {
//ClassCastException: Parent cannot be cast to Child
System.out.println(child);
}
}
}
//2.Scala 泛型装载
import scala.collection.mutable.ListBuffer
//非集合类 泛型用途:定义变量,定义函数参数
class Student[T] {
var aaa:T = _
def test(ttt : T): Unit ={
}
}
class Parent {}
class Child extends Parent { var a = 10}
class SubChild extends Child {}
object Scala05_TestGeneric {
def main(args: Array[String]): Unit = {
//1.如果不声明泛型,会直接为 Nothing,无法加入任何对象
var studs= ListBuffer[Parent]()
studs.append(new Parent)
//2.Scala任何对象的泛型都是一开始确定的,不可改变的
var childs: ListBuffer[Child] = ListBuffer[Child]()
//3.两 List 容器装载时,必须泛型相同才可赋值
//childs = studs
}
}
上下文限定是将泛型和隐式转换的结合产物
//基本语法:
def f[A : B](a: A) = println(a) //简写
def f[A](a:A)(implicit arg:B[A]) = println(a) //完整
实质 => 柯里化的嵌套函数,对内层函数隐式参数的 泛型 进行了限定,隐式参数可再指定?
//实例解析
object Scala06_TestGeneric {
def main(args: Array[String]): Unit = {
println(f2(1, 2))
}
//def 函数名[参数泛型 : 隐式参数类型](泛型参数a,泛型参数b) = 隐式参数类型实例.方法(a,b)
//注:使用[A:Ordering],方法内无法使用隐式参数名调用隐式参数,需通过 implicitly[Ordering[A]] 获取隐式变量
def f1[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
/*def 函数名[参数泛型]
(柯里化第1层:泛型参数a,泛型参数b)
(柯里化第2层: implicit 隐式参数:隐式参数类型[参数泛型]) = 隐式参数.方法(a,b)*/
def f2[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
}