Scala -scala进阶- 样例类、模式匹配、偏函数

本文深入探讨Scala的样例类和模式匹配概念,包括样例类的特点、与普通类的区别、实战应用以及如何通过模式匹配进行值、类型、集合的匹配。同时,介绍了unapply方法在模式匹配中的作用,展示了如何通过偏函数处理部分数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

scala进阶

一、样例类(重点)

1.1、样例类介绍

1.2、样例类特点

1.3、样例类与普通类的区别

1.4、样例类的实战

1.5、普通类模仿样例类

二、模式匹配(重点)

2.1、简单介绍

2.2、值匹配

2.2.1、值匹配实战

2.2.2、Scala对于常量还是变量模式的规则

2.3、类型匹配

2.4、集合匹配

2.4.1、字符串匹配

2.4.2、数组匹配

2.4.3、列表匹配

2.4.4、元组匹配

2.5、样例类匹配

2.6、unapply提取器

2.6.1、unapply规则:

2.6.2、unapply的实战

2.7、总结apply和unapply的区别

2.8、函数匹配(拓展)

三、偏函数

3.1、说明

3.2、偏函数实战


scala进阶

一、样例类(重点)

1.1、样例类介绍

1、“case class”一般被翻译成样例类,它是一种特殊的类,样例类是不可变的,可以通过值进行比较,可用于模式匹配。

2、样例类的定义方式:
       case class 样例类名字(主构造器) 
     注:主构造器可加可不加,不加的话默认加

1.2、样例类特点

1、自动生成伴生对象,自动生成伴生关系。

2、自动生成的伴生对象object提供apply方法equals,unapply...),进而可以让我们不使用new关键字就能构造出相应的对象,并返回一个class实例。

3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段),自动生成类的setter和getter构造器。

1.3、样例类与普通类的区别

1、很显然,样例类有case关键字修饰,而普通类没有

2、样例类的一个优点是方便输出,反而普通类不方便,代码如下:

scala> case class Student(name:String,age:Int)
defined class Student

scala> println(Student("xiaofeng",18))
Student(xiaofeng,18)

scala> class Student1(name:String,age:Int)
defined class Student1

scala> println(new Student1("xiaoxiao",18))
$line81.$read$$iw$$iw$Student1@487d4820

1.4、样例类的实战

package com.scala.caseclass

/**
 *样例类特点:
 * 1、自动生成伴生关系
 * 2、自动生成的object对象,提供apply方法(equals,unapply...),并返回一个class实例
 * 3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段)
 *       自动生成类的setter和getter构造器
 */
case class Student(name:String, age: Int)

//特点一: 构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段)
object CaseClassDemo {
  def main(args: Array[String]): Unit = {

    //创建样例类对象
    val stu1 = new Student("jasss",18)
//    stu1.name="liq"  //因为主构造器的参数默认为val  所以无法修改值,并不是对象私有字段
    val stu2 = Student("jasss",20)
    val stu3 = Student.apply("jasss",20)

    //访问对象值
    println(stu2.name,stu2.age,args)  //可以直接访问
//    stu2.name = "xiaofeng"  //修改值就不行 因为是val修饰的变量 相当于java中的常量

    println("------------------------")
    /*
      样例类,它是一种特殊的类,样例类是不可变的,可以通过值进行比较
      普通类的话,比较的是对象的引用,所以肯定是不同的
     */
    if (stu1==stu2)
      println(stu1 + " and " + stu2 + " are the same object")
    else
      println(stu1 + " and " + stu2 + " are not the same object")

    if (stu2==stu3)
      println(stu2 + " and " + stu3 + " are the same object")
  }
}

 

1.5、普通类模仿样例类

package com.scala.caseclass

//样例类
case class CaseClassDemo2(name:String,age:Int)
//等价的普通类
class CaseClassDemo1(val name:String,val age:Int) {
  override def toString: String = {
    (name,age).toString()
  }

  override def equals(obj: Any): Boolean = super.equals(obj)
  //....
}
object CaseClassDemo1 extends App {
  def apply(name: String, age: Int): CaseClassDemo1 = new CaseClassDemo1(name, age)

  /*
  //提取器的第一个功能:完成对象参数的提取
  def unapply(arg: User): Option[(String, Int)] = {
    if(arg!=null)
      Some((arg.name,arg.age))
    else
      None
  }
  */
  //提取器的第二个功能,测试一个对象
  def unapply(arg: CaseClassDemo1): Boolean= if(arg.age>10) true else false
}

二、模式匹配(重点)

2.1、简单介绍

1、Scala是没有Java中的switch case语法的,但Scala提供了更加强大的match case语法,即模式匹配。

2、
  Scala的match case与Java的switch case最大的不同点在于,Java的switch case能匹配变量的值,比1、2、3等;
  而Scala的match case可以匹配各种情况,比如变量的类型、集合的元素、有值或无值等。

2.2、值匹配

2.2.1、值匹配实战

//提示:其实不论常量匹配还是变量匹配,都是值匹配,自我感觉没啥区别,也没必要特意在意记住什么的
//常量
scala> val char = 'p'
char: Char = p

scala> val ch = char
ch: Char = p

scala> ch match{case 'p'=>println("1111"); case 'y'=>println("222");case _ =>println("333")}
1111

scala> ch match{case 'y'=>println("1111") case 'p'=>println("222") case _ =>println("333")}
222

//总结:其实加不加分号;都是一样的,只要执行了一个case,就不会执行其它的了,反而java中的switch case中需要break进行中断

//变量
scala> import Math.PI
import Math.PI

scala> val ch=Math.PI
ch: Double = 3.141592653589793

scala> ch match {case PI=>println(PI) case _=>println("others")}
3.141592653589793

scala> ch match {case x=>println(ch)}
3.141592653589793

scala> ch match {case y => println("this is "+ch) case PI=>println(PI)}
<console>:15: warning: patterns after a variable pattern cannot match (SLS 8.1.1)
       ch match {case y => println("this is "+ch) case PI=>println(PI)}
                      ^
<console>:15: warning: unreachable code due to variable pattern 'y' on line 15
       ch match {case y => println("this is "+ch) case PI=>println(PI)}
                                                                  ^
<console>:15: warning: unreachable code
       ch match {case y => println("this is "+ch) case PI=>println(PI)}
                                                                  ^
this is 3.141592653589793

scala> val ch = 'a'
ch: Char = a

scala> val y = 'b'
y: Char = b

scala> ch match {case PI=>println(PI); case y => println("this is "+ch)}
this is a


2.2.2、Scala对于常量还是变量模式的规则

1、以小写字母开头的名称为变量模式
2、如果常量符号也是小写字母开头,则需要使用反引号括起来:`i`

注意:是反引号` `,而不是单引号‘ ’

2.3、类型匹配

注意: 当你在匹配类型的时候,必须给出一个变量名,否则你将会拿对象本身来进行匹配 。

package com.scala.typeMmatch 

import scala.util.Random

object TypeMatchDemo extends App {

  //1、

//  val arr = Array("this is string",2021,1.0,'a')
//  var i= 0
//  while(i<10){
//    val obj = arr(Random.nextInt(4))
//    obj match {
//      case a:Int => println(a)
//      case b:String => println(b.toUpperCase)
//      case _:Double => println(Int.MaxValue)
//      case _ =>println("0")
//    }
//    i+=1
//  }

  //2、

//  class Person
//  class Student(val name:String) extends Person
//  val stu=new Student("xiaofeng")
//
//  println(stu.getClass)
//  println(stu.isInstanceOf[Person])
//  println(stu.isInstanceOf[Student])
//
//  stu match {
//    case x:Person=>println("person")
//    case x:Student =>println(x.name)  //因为有第一个cse,所以成为无法匹配的代码
//  }

  //3、其实下面两个 都可以 
  //类型匹配好像不看泛型,此处有疑惑
  val lst=List[Any](1,2,3)

  lst match {
    case x:List[Int]=>println(x)
  }

//  val lst=List[Int](1,2,3)
//
//  lst match {
//    case x:List[Any]=>println(x)
//  }


}

2.4、集合匹配

2.4.1、字符串匹配

/**
  * 模式匹配-字符串
  */
object StringMatchDemo {
  def main(args: Array[String]): Unit = {
    val arr = Array("zhoudongyu", "yangzi", "guanxiaotong", "zhengshuang")
      
    val name = arr(Random.nextInt(arr.length))
    println(name)
      
    name match {
      case "zhoudongyu" => println("周冬雨")
      case "yangzi" => println("杨紫")
      case "guanxiaotong" => println("关晓彤")
      case "zhengshuang" => println("郑爽")
      case _ => println("Nothing ...")
    }

  }
}

2.4.2、数组匹配

代码1:

package com.scala.arraymatch

object ArrayMatchDemo extends App {
  val arr1 = Array(0,0,0)
//  println(Array(0))
  println(arr1.isInstanceOf[Array[Int]])

  val res = arr1 match {
    case Array(0) => println(Array(0)(0))// == arr1(0) //匹配1个0的数组
    case Array(x, y) => println(s"$x $y") // 匹配任何带有两个元素的数组,并将元素绑定到x和y
    case Array(0, _*) => println("0...") //匹配任何以0开始的数组
    case _ => println("something else")
  }
  println(res.getClass)

}

代码2:

package com.scala.arraymatch

object ArrayMatchDemo1 extends App {
  val arr = Array(1,2,3,4)
  arr match {
      //匹配数组中的前两个元素
//    case Array(x,y,_*)=>println(s"$x,$y")  //输出 1,2
    //匹配数组中的第三个元素
//    case Array(_,_,x,_*)=>println(s"$x") //输入3
      //匹配第一个元素
    case Array(x,tail@_*)=>println(s"$x,$tail") //输出 1,Vector(2, 3, 4)
  } // 任意字符串@都可以


}

2.4.3、列表匹配

package com.scala.listmatch

object ListMatchDemo extends App {
  val list = List(1,2,3,4)
  list match {
//    case List(x,y,_*)=>println(s"$x,$y") //匹配数组中的前两个元素  输出 1,2
//    case List(_,_,x,_*)=>println(s"$x") //匹配数组中的第三个元素 输出3
    case List(x,zz@_*) =>println(s"$x,$zz") //输出 1,List(2, 3, 4)
  }
}

2.4.4、元组匹配

package com.scala.tuplematch

object TupleMatchDemo extends App {
  val tuple = (1,2,3,4)
  tuple match {
    //    case(x,y,_*)=>println(s"$x,$y") // 编译错误,元组不支持_*
    case (x,y,z,_) =>println(x,y,z) //输出(1, 2, 3)
    case _=>println("others")
  }
}

2.5、样例类匹配

 总结:
    样例类匹配其实与普通类匹配一样的,只不过匹配需要 unapply方法
    普通类匹配 需要unapply方法 才可以匹配,也就是说需要我们自己手动重写unapply方法
    样例类匹配 则是自动提供了unapply方法,并且已经隐式的调用了
    并且什么集合匹配 元组匹配 数组匹配 都隐式的调用了unapply方法

package com.scala.caseclassmatch

/**
 * 样例类匹配其实与普通类匹配一样的,只不过匹配需要 unapply方法
 *  普通类匹配 需要unapply方法 才可以匹配,也就是说需要我们自己手动重写unapply方法
 *  样例类匹配 则是自动提供了unapply方法,并且已经隐式的调用了
 *  并且什么集合匹配 元组匹配 数组匹配 都隐式的调用了unapply方法
 *
 */
object CaseClassMatchDemo extends App{

  //1、普通类的模式匹配
  val cat1 = new Cat1("xiaofeng",18)
  //普通类的模式匹配
  cat1 match {
    case Cat1(name,id) => println(name,id)
  }

  //2、普通类的模式匹配
  val cat = Cat("xiaoxiao",11)
  cat match {
    case Cat(name,id) => println(name,id)
  }

}
//1、先看一下普通类匹配
class Cat1(val name:String,val id:Int)

object Cat1{
    def unapply(arg: Cat1): Option[(String, Int)] = {
      if (arg == null)
        None //Some(("",0))
      else
        Some((arg.name,arg.id))
    }
}

//2、样例类匹配
case class Cat(val name:String,val id:Int)

2.6、unapply提取器

2.6.1、unapply规则:

1、case 中对象的unapply方法返回Some集合则代表这个对象匹配成功。
2、反之如果返回为none集合则匹配失败。

2.6.2、unapply的实战

package com.scala.unapply

import scala.util.Random

class UnapplyDemo { }

object UnapplyDemo {
  // apply的构造是返回一个name+数字的字符串
  def apply(name:String):String = name+Random.nextLong()
  def unapply(name:String): Option[String] ={
    if(name.nonEmpty) Some(name) else None
  }

  def main(args: Array[String]): Unit = {
    //创建一个对象,此时会触发apply方法
    val c = UnapplyDemo("姜小白")
    println(c)

    //模式匹配 用来判断UnapplyDemo对象是否创建成功
    /* 当然这是自定义的apply 返回的 值,实际上其他的匹配,apply返回是什么不一定
    1、当将UnapplyDemo(name)写在case后时 就会默认触发伴生类中unapply方法
        name 值会被传递到 def unapply(name:String)方法中就相当于将name赋值给了形参的name
        如果判断对象是否创建成功 -->只要返回Some集合就认为创建成功后
        开始执行 if判断语句 name.nonEmpty(不是空的) 返回some集合
        若为空 返回None
    case语句后来判断是否创建成功 */

    c match {
      case UnapplyDemo(name)=>println(name+"!!!")
      case _=>println("没有")
    }
  }
}

 

2.7、总结apply和unapply的区别

# apply
- apply方法通常会被称为注入方法(创建对象),在类的伴生对象中做以些初始化的操作 
- apply方法的参数列表不需要和原生类的构造方法参数一致 
# unapply
- unapply方法通常被称为提取方法,使用unapply方法可以提取固定数量的对象或值 
- unapply方法返回的是一个Option类型,因为unapply除了提取值之外,还有一个目的就是判断对象是否创建成功。若成功则有值,反之没有值,所有才使用Option做为返回值类型 

ps:只要是object类,默认都是提供apply和unapply方法,是被默认隐式调用

2.8、函数匹配(拓展)

package com.scala.functionmatch

/**
 * 根据模式匹配写一下函数匹配
 */
object FunctionMatchDemo extends App {
  val ff =(x:Int,y:Int)=>x+y   //很常见

  ff match {
    case f:((Int,Int)=> Int)=> println(f(2,8))  //输出10
  }

  val ff1:(Int,Int)=>Int = (x,y)=>x+y  //也挺常见

  ff1 match {
    case f1:((Int,Int)=> Int)=> println(f1(2,9)) //输出11
  }
//  val ff2 =(x,y)=>x+y   //这么简写不行,在只有一个参数的时候可以这样写(只有一个参数也不能这样写,刚试验了)
  val ff2 =(_:Int)+(_:Int)

  ff2 match {
    case f:((Int,Int)=> Int)=> println(f(2,10)) //输出12
  }
}

 

三、偏函数

3.1、说明

1、只对集合中的一部分数据进行处理的函数就是偏函数(scala.PartialFunction)

2、被包在花括号内没有match的一组case语句是一个偏函数(也就是说case表达式返回偏函数),它是PartialFunction[A, B]的一个实例,A代表输入参数类型,B代表返回类型,常用作输入模式匹配

3、PartialFunction是一个特质(详情请看源码),包括两个方法
     a) isDefinedAt()方法: 对哪些数据进行处理
     b) apply()方法:如何处理  

3.2、偏函数实战

package com.scala.partialfuction

/**
 * 如果使用偏函数对偶数进行处理
 */

object PartialFuctionDemo extends App {

  val pf = new PartialFunction[Int,Double] {
    override def isDefinedAt(x: Int): Boolean = {
      x%2 == 0 //返回true 则表示定义对偶数进行处理
    }

    override def apply(v1: Int): Double = {
      v1.toDouble
    }
  }

//  println(pf(111))

  List(1,2,3,4).collect(pf).foreach(println(_))

  println(pf.applyOrElse(222,(x:Int)=>0d))

  //总结:collect本质上也是调用applyOrElse方法,而applyOrElse则是先调用isDefinedAt 后调用apply
  // 若调用applyOrElse 返回true 则调用apply
  // 若调用applyOrElse 返回false 则不调用apply,不进行处理 返回默认的值
  
  //case 表达式返回的也是一个偏函数
  List(1,2,3,4).collect({case y:Int if y%2==0 => y.toDouble}).foreach(println(_))
  

}

collect: 收集一个弹性分布式数据集的所有元素到一个数组中,这样便于我们观察,毕竟分布式数据集比较抽象

被包括在花括号中的case表达式返回的是一个偏函数,实例如下:

scala> val pf = {case x:Int if x%2==0 =>x.toDouble}
<console>:11: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
       val pf = {case x:Int if x%2==0 =>x.toDouble}
                ^

scala> val pf:PartialFunction[Int,Double] = {case x:Int if x%2==0 =>x.toDouble}
pf: PartialFunction[Int,Double] = <function1>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值