1. 样例类
简介:
类似java中的实体类
case class 样例类名([var/val] 成员变量名1:
类型1, 成员变量名2:类型2, 成员变量名3:类型3)
样例类的方法:
apply方法
可以让我们快速使用类名创建对象
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
}
}
unapply方法
让我们可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配 的关键方法
又叫解构器
就是将对象中的各个属性值,分别提取出来。底层是依赖unapply方法。
override def unapply(stu:Student):Option[(类型1, 类型2, 类型3...)] = { if(stu != null) {
Some((变量1, 变量2, 变量3...)) }
else { None
} }
/**
* @program: sz22scala
* @description
* @author: chenjia868
* @create: 2021-03-23 14:46
* */
object Demo16Extractor { //创建一个Student类,包含姓名年龄两个字段
class Student (var name:String,var age:Int) //定义半生对象,重写unapply方法,实现一个类的解构器, object Student{
def unapply(stu:Student)={
if(stu!=null){
Some(Tuple2(stu.name,stu.age))
}else{
None }
} }
def main(args: Array[String]): Unit = { //并使用match表达式进行模式匹配,提取类中的字段。。 val stu = new Student("张三",20)
stu match {
case Student(name,age)=>{
println(s"name=${name},age=${age}")
} }
} }
toString方法
方便查看样例类成员
equals方法
可以直接用==比较两个样例类是否相等,既所有的成员变量是否相等
hashCode方法
如果所有成员变量值相同,则hash值相同,只要有一个不一样,则hash值不一样.
copy方法
可以快速的创建一个相同的实力对象.可以使用带名参数指定给成员进行重新赋值
创建名字年龄分别为"李四", 21的对象
通过copy拷贝,名字为"王五"的对象
**示例**
- 创建名字年龄分别为"李四", 21的对象
- 通过copy拷贝,名字为"王五"的对象
2. 样例对象
跟java的枚举是一样的用法.
只要一个类名有意义.不需要属性跟方法
case object 样例对象名
object Demo2CaseObject {
3. 模式匹配
java中的模式匹配是switch case,如果外界变量命中的一个case,如果不主动break,则会继续匹配下一个
case
scala中的模式匹配是 match case,如果外界变量命中的一个case,会自动break出去。 java只支持等值匹配,scala的模式匹配支持很多灵活的业务场景
语法
需求案例
变量 match {
case "常量1" => 表达式1 case "常量2" => 表达式2 case "常量3" => 表达式3 case _ => 表达式4
}
// 默认配
object Dog{
val name="wangcai"
def say()=println("wangwang")
} //去掉属性和方法,就是样例对象 case object Dog2
//创建一个Trait,表示性别Sex枚举,
trait Sex //定义它的两个实例,样例对象(男性——Male、女性——Female) case object Male extends Sex //定义一个Person样例类,它有两个成员(姓名、性别)
case object Female extends Sex
case class Person(name:String,sex:Sex)
def main(args: Array[String]): Unit = {
//创建两个Person对象("张三"、男性)、("李四"、"女") val p1 = Person("张三",Male)
val p2 = Person("李四",Female)
println(p1,p2)
} }
3. 模式匹配
语法
变量 match{
case 类型1变量名: 类型1 => 表达式1
case _=> 表达式4
}
示例
定义一个变量为Any类型,然后分别给其赋值为"hadoop"、1、1.0
定义模式匹配,然后分别打印类型的名称
val a:Any = "hadoop"
val result = a match {
case _:String => "String"
case _:String => "String"
}
println(result)
守卫
就是java中case方法里的default,也就是说在没有匹配到值得情况下得取一个默认的值
val a = StdIn.readInt()
a match {
case _ if a >= 0 && a <= 3 => println("[0-3]")
case _ if a >= 4 && a <= 8 => println("[3-8]")
case _ => println("未匹配")
}
匹配样例类
scala可以使用模式匹配来匹配样例类,从而可以快速获取样例类的对象中的成员数据
声明变量中的模式匹配:在一行代码中快速的定义多个变量,并且给他们赋值。
obj match { //一举2得,匹配了类型,也获取了各个属性值
case Customer(name,age)=>
{ println(s"它是Customer类,且name=${name},age=${age}")
} }
4. Option类型
使用Option类型,可以用来有效避免空引用(null)异常。也就是说,将来我们返回某些数据时,可以返回一个Option类型来替代。
-
Some(x):表示实际的值
-
None:表示没有值
-
使用getOrElse方法,当值为None是可以指定一个默认值
/**
* 定义除法操作
* @param a 参数1
* @param b 参数2
* @return Option包装Double类型
*/
def dvi(a:Double, b:Double):Option[Double] = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result1 = dvi(1.0, 5)
result1 match {
case Some(x) => println(x)
case None => println("除零异常")
}
}
def dvi(a:Double, b:Double) = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result = dvi(1, 0).getOrElse(0)
println(result)
}
5. 偏函数
偏函数可以提供了简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更加优雅。
-
偏函数被包在花括号内没有match的一组case语句是一个偏函数
-
偏函数是PartialFunction[A, B]的一个实例
-
A代表输入参数类型
-
B代表返回结果类型
// func1是一个输入参数为Int类型,返回值为String类型的偏函数
val func1: PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
println(func1(2))
示例说明
- 定义一个列表,包含1-10的数字
- 请将1-3的数字都转换为[1-3]
- 请将4-8的数字都转换为[4-8]
- 将其他的数字转换为(8-*]
val list = (1 to 10).toList
val list2 = list.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case x if x > 8 => "(8-*]"
}
println(list2)
6.正则表达式
Regex类
-
scala中提供了Regex类来定义正则表达式
-
要构造一个RegEx对象,直接使用String类的r方法即可
-
建议使用三个双引号来表示正则表达式,不然就得对正则中的反斜杠来进行转义
val regEx = """正则表达式""".r
findAllMatchIn方法
- 使用findAllMatchIn方法可以获取到所有正则匹配到的字符串
val r = """.+@.+\..+""".r
val eml1 = "qq12344@163.com"
val eml2 = "qq12344@.com"
if(r.findAllMatchIn(eml1).size > 0) {
println(eml1 + "邮箱合法")
}
else {
println(eml1 + "邮箱不合法")
}
if(r.findAllMatchIn(eml2).size > 0) {
println(eml2 + "邮箱合法")
}
else {
println(eml2 + "邮箱不合法")
}
7. 异常处理
def main(args: Array[String]): Unit = {
val i = 10 / 0
println("你好!")
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
try {
// 代码
}
catch {
case ex:异常类型1 => // 代码
case ex:异常类型2 => // 代码
}
finally {
// 代码
}
- try中的代码是我们编写的业务处理代码
- 在catch中表示当出现某个异常时,需要执行的代码
- 在finally中,是不管是否出现异常都会执行的代码
try {
val i = 10 / 0
println("你好!")
} catch {
case ex: Exception => println(ex.getMessage)
}
抛出异常
def main(args: Array[String]): Unit = {
throw new Exception("这是一个异常")
}
Exception in thread "main" java.lang.Exception: 这是一个异常
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
8. 提取器(Extractor)
之前我们学习过了,实现一个类的伴生对象中的apply方法,可以用类名来快速构建一个对象。伴生对象中,还有一个unapply方法。与apply相反,unapply是将该类的对象,拆解为一个个的元素。
示例说明
- 创建一个Student类,包含姓名年龄两个字段
- 实现一个类的解构器,并使用match表达式进行模式匹配,提取类中的字段。
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("张三", 20)
zhangsan match {
case Student(name, age) => println(s"${name} => ${age}")
}
}
9. 泛型
scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。
scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)
语法
def 方法名[泛型名称](..) = {
//...
}
不考虑泛型
ef getMiddle(arr:Array[Int]) = arr(arr.length / 2)
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4,5)
println(getMiddle(arr1))
}
加入泛型之后
def getMiddleElement[T](array:Array[T]) =
array(array.length / 2)
def main(args: Array[String]): Unit = {
println(getMiddleElement(Array(1, 2, 3, 4, 5)))
println(getMiddleElement(Array("a", "b", "c", "d", "e")))
}
泛型类
scala的类也可以定义泛型。接下来,我们来学习如何定义scala的泛型类
语法
class 类[T](val 变量名: T)
示例说明
- 实现一个Pair泛型类
- Pair类包含两个字段,而且两个字段的类型不固定
- 创建不同类型泛型类对象,并打印
case class Pair[T](var a:T, var b:T)
def main(args: Array[String]): Unit = {
val pairList = List(
Pair("Hadoop", "Storm"),
Pair("Hadoop", 2008),
Pair(1.0, 2.0),
Pair("Hadoop", Some(1.9))
)
println(pairList)
}
泛型的上下界
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Policeman))
// 编译出错:Superman是Policeman的子类
// demo(Array(new Superman))
}
协变、逆变、非变 (掌握)
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 编译报错,无法将p1转换为p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
示例
- 定义一个Super类、以及一个Sub类继承自Super类
- 使用协变、逆变、非变分别定义三个泛型类
- 分别创建泛型类来演示协变、逆变、非变
class Super
class Sub extends Super
class Temp1[T]
class Temp2[+T]
class Temp3[-T]
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 编译报错
// 非变
//val b:Temp1[Super] = a
// 协变
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆变
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}
10. 隐式转换和隐式参数
隐式转换
-
隐式转换方法,可以让一个对象,转换成一个新对象,这个新对象拥有额外的我们定义的行为。
-
隐式转换方法,需要定义一个方法,只接受一个变量,这个变量就是我们需要转换的那个对象,该方法用implicit关键字来修饰。
-
隐式转换方法需要定义在object中 在需要用到隐式转换的地方,手动引入隐式
-
转换(使用import) 编译器自动调用隐式转化后的方法
import java.io.File
object demoStudent {
def main(args: Array[String]): Unit = { //5.main方法中,定义文件对象
val file12 = new File("/Users/ttony/Desktop/back/deom/data/1.txt")
//6.手动引入RichFile的file2RichFile方法 //import RichFile.file2RichFile(file:File)
//如果在当前同一个作用域中有隐式转换方法,会自动导入隐式转换。
implicit def file2RichFile(a: File) = new RichFile(file12)
//7.在file上调用read()方法,打印结果
//当对象调用类中不存在的方法或者成员时,编译器会自动将对象进行隐式转换
println(file12.read())
}
}
import java.io.File
import scala.io.Source
class RichFile (file: File){
def read()=Source.fromFile(file).mkString
}
object RichFile{
//4.伴生对象中定义一个方法file2RichFile,返回RichFile的一个对象实例,这个方法用implicit修饰。
implicit def file2RichFile(file:File)=new RichFile(file)
}
隐式参数
-
在一个柯里化方法,有多个参数列表,其中最后一个列表,可以做成隐式参数,这个参数需要用implicit关 键字来修饰
-
在一个object中定义一个implicit修饰的默认的 隐式值(是成员属性),后期可以自动的被导入到上面定义 的隐式参数中
-
和隐式转换一样,可以使用import手动导入2中的默认的隐式参数
-
调用1中的柯里化方法时,可以不传入implicit修饰的参数列表,编译器会自动查找缺省值
object demoStudent {
////1.定义一个柯里化方法quote(String)(Tuple),第二个参数用implicit修饰。
// 可将传入的值,使用一个分隔符前缀、后缀包括起来
def quot(str:String)(implicit del:(String,String)) ={
del._1 + str + del._2
}
def main(args: Array[String]): Unit = {
import DelimiterParam.DEFAULT_DELIMITER
println(quot("你好"))
}
}
// 定义一个object,包含一个成员变量,用implicit修饰,
// 表示隐式参数,也是步骤1的潜在的分隔符
object DelimiterParam {
implicit val DEFAULT_DELIMITER:(String,String) = ("<<<",">>>")
}
网友评论