scala快速入门--Option类型、偏函数、正则表达式、异常处理、提取器、泛型的使用方法

scala快速入门–Option类型、偏函数、正则表达式、异常处理、提取器的使用方法

在这里插入图片描述

1、Option类型

使用Option类型,可以用来有效避免空引用(null)异常。也就是说,将来我们返回某些数据时,可以返回一个Option类型来替代。

scala中,Option类型来表示可选值。这种类型的数据有两种形式:

  • Some(x):表示实际的值
    在这里插入图片描述
  • None:表示没有值
    在这里插入图片描述
  • 使用getOrElse方法,当值为None是可以指定一个默认值

示例一
示例说明

  • 定义一个两个数相除的方法,使用Option类型来封装结果
  • 然后使用模式匹配来打印结果
    • 不是除零,打印结果
    • 除零打印异常错误

参考代码

  /**
    * 定义除法操作
    * @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("除零异常")
    }
  }

示例二
示例说明

  • 重写上述案例,使用getOrElse方法,当除零时,或者默认值为0

参考代码

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)
}

2、偏函数

偏函数可以提供了简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更加优雅。

定义

  • 偏函数被包在花括号内没有match的一组case语句是一个偏函数

  • 偏函数是PartialFunction[A, B]的一个实例

    • A代表输入参数类型
    • B代表返回结果类型

示例一
示例说明

定义一个偏函数,根据以下方式返回

输入返回值
1
2
3
其他其他

参考代码

// 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)

3、正则表达式

在scala中,可以很方便地使用正则表达式来匹配数据。
定义

Regex类

  • scala中提供了Regex类来定义正则表达式

  • 要构造一个RegEx对象,直接使用String类的r方法即可

  • 建议使用三个双引号来表示正则表达式,不然就得对正则中的反斜杠来进行转义

    val regEx = """正则表达式""".r
    

findAllMatchIn方法

  • 使用findAllMatchIn方法可以获取到所有正则匹配到的字符串

示例一
示例说明

  • 定义一个正则表达式,来匹配邮箱是否合法
  • 合法邮箱测试:qq12344@163.com
  • 不合法邮箱测试:qq12344@.com

参考代码

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 + "邮箱不合法")
}

示例二
示例说明

找出以下列表中的所有不合法的邮箱

"38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com"

参考代码**

val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")

val regex = """.+@.+\..+""".r

val invalidEmlList = emlList.filter {
    x =>
    if (regex.findAllMatchIn(x).size < 1) true else false
}

println(invalidEmlList)

示例三
示例说明

  • 有以下邮箱列表

    "38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com"
    
  • 使用正则表达式进行模式匹配,匹配出来邮箱运营商的名字。例如:邮箱zhansan@163.com,需要将163匹配出来

    • 使用括号来匹配分组
  • 打印匹配到的邮箱以及运营商

参考代码

// 使用括号表示一个分组
val regex = """.+@(.+)\..+""".r

val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")

val emlCmpList = emlList.map {
    case x@regex(company) => s"${x} => ${company}"
    case x => x + "=>未知"
}

println(emlCmpList)

4、异常处理

来看看下面一段代码。

  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)

执行程序,可以看到scala抛出了异常,而且没有打印出来"你好"。说明程序出现错误后就终止了。

那怎么解决该问题呢?

在scala中,可以使用异常处理来解决这个问题

4.1、捕获异常

语法格式

try {
    // 代码
}
catch {
    case ex:异常类型1 => // 代码
    case ex:异常类型2 => // 代码
}
finally {
    // 代码
}
  • try中的代码是我们编写的业务处理代码
  • 在catch中表示当出现某个异常时,需要执行的代码
  • 在finally中,是不管是否出现异常都会执行的代码

示例
示例说明

  • 使用try…catch来捕获除零异常

参考代码

try {
    val i = 10 / 0

    println("你好!")
} catch {
    case ex: Exception => println(ex.getMessage)
} 
4.2、抛出异常

我们也可以在一个方法中,抛出异常。语法格式和Java类似,使用throw new Exception...

示例 | 抛出异常
示例说明

  • 在main方法中抛出一个异常

参考代码

  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)
  • scala不需要在方法上声明要抛出的异常,它已经解决了再Java中被认为是设计失败的检查型异常。

5、提取器(Extractor)

我们之前已经使用过scala中非常强大的模式匹配功能了,通过模式匹配,我们可以快速匹配样例类中的成员变量。例如:

// 1. 创建两个样例类
case class Person(name:String, age:Int)
case class Order(id:String)

def main(args: Array[String]): Unit = {
    // 2. 创建样例类对象,并赋值为Any类型
    val zhangsan:Any = Person("张三", 20)
    val order1:Any = Order("001")

    // 3. 使用match...case表达式来进行模式匹配
    // 获取样例类中成员变量
    order1 match {
        case Person(name, age) => println(s"姓名:${name} 年龄:${age}")
        case Order(id1) => println(s"ID为:${id1}")
        case _ => println("未匹配")
    }
}

那是不是所有的类都可以进行这样的模式匹配呢?答案是:

不可以的。要支持模式匹配,必须要实现一个提取器

[!NOTE]

样例类自动实现了apply、unapply方法

5.1、定义提取器

之前我们学习过了,实现一个类的伴生对象中的apply方法,可以用类名来快速构建一个对象。伴生对象中,还有一个unapply方法。与apply相反,unapply是将该类的对象,拆解为一个个的元素。

在这里插入图片描述
要实现一个类的提取器,只需要在该类的伴生对象中实现一个unapply方法即可。
语法格式

def unapply(stu:Student):Option[(类型1, 类型2, 类型3...)] = {
    if(stu != null) {
        Some((变量1, 变量2, 变量3...))
    }
    else {
        None
    }
}

示例

示例说明

  • 创建一个Student类,包含姓名年龄两个字段
  • 实现一个类的解构器,并使用match表达式进行模式匹配,提取类中的字段。

参考代码

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("张三", 20)

    zhangsan match {
        case Student(name, age) => println(s"${name} => ${age}")
    }
}

6、泛型

scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。

scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)

那如何自己定义泛型呢?

6.1、定义一个泛型方法

在scala中,使用方括号来定义类型参数。

语法格式

def 方法名[泛型名称](..) = {
    //...
}

示例

示例说明

  • 用一个方法来获取任意类型数组的中间的元素
    • 不考虑泛型直接实现(基于Array[Int]实现)
    • 加入泛型支持

参考代码

不考虑泛型的实现

  def 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))
  }
6.2、泛型类

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)
}
6.3、泛型的上下界

需求:

我们在定义方法/类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时,就需要使用到上下界。

6.3.1、上界定义

使用<: 类型名表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承

语法格式

[T <: 类型]

示例
示例说明

  • 定义一个Person类
  • 定义一个Student类,继承Person类
  • 定义一个demo泛型方法,该方法接收一个Array参数,
  • 限定demo方法的Array元素类型只能是Person或者Person的子类
  • 测试调用demo,传入不同元素类型的Array

参考代码

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("hadoop"))
}
6.3.2、下界定义

上界是要求必须是某个类的子类,或者必须从某个类继承,而下界是必须是某个类的父类(或本身)

语法格式

[T >: 类型]

[!NOTE]
如果类既有上界、又有下界。下界写在前面,上界写在后面

示例
示例说明

  • 定义一个Person类
  • 定义一个Policeman类,继承Person类
  • 定义一个Superman类,继承Policeman类
  • 定义一个demo泛型方法,该方法接收一个Array参数,
  • 限定demo方法的Array元素类型只能是Person、Policeman
  • 测试调用demo,传入不同元素类型的Array

参考代码

class Person
class Policeman extends Person
class Superman extends Policeman

def demo[T >: Policeman](array:Array[T]) = println(array)

def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的子类
    // demo(Array(new Superman))
}
6.4、协变、逆变、非变
6.4.1、非变

语法格式

class Pair[T]{}
  • 默认泛型类是非变的
  • 类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
  • Java是一样的

在这里插入图片描述

6.4.2、协变

语法格式

class Pair[+T]
  • 类型B是A的子类型,Pair[B]可以认为是Pair[A]的子类型
  • 参数化类型的方向和类型的方向是一致的。
6.4.3、逆变

语法格式

class Pair[-T]
  • 类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
  • 参数化类型的方向和类型的方向是相反的

示例
示例说明

  • 定义一个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
}

好了,以上文章内容到这里就结束了各位的点赞,关注就是小编坚持下去的动力。小编本着“以诚会友”的人道主义精神,来分享更多的知识哦。。
我是小哪吒,个性签名:“我们不生产代码,我们只做代码的搬运工”~~~~哈哈哈

不要因为希望去坚持,要坚持的看到希望。
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值