Scala 2.1 —— Scala OOP Extension
一、正则
文章目录
Scala 2.1 —— Scala OOP Extension一、正则1.1 Java正则和Scala正则的区别1.2 Java正则和Scala正则的的基本知识点Java正则Scala正则 1.3 练习练习一:使用正则表达式解析日志方法一:使用`findAllMatchIn`方法提取出所有符合正则且可按组匹配的对象构成一个迭代器,后通过`map`方法将每一行中符合的部分提取出来构成元组。方法二:使用`collect`方法在传入的偏函数中构造模式匹配,将匹配的部分提取出来构成元组 练习二:提取字符串的每组数据 二、隐式类三、异常3.1.Java的基本异常处理结构`try-catch-finally``try-with-resources`对比 3.2.Scala的异常处理机制Option类型Either类型Try类型值获取方式catch的模式匹配 3.3.Scala的异常控制工具allCatch工具failAsValue工具 四、类型信息处理4.1 定义类和子类4.2 实例化和使用4.3 类型判断和检查
1.1 Java正则和Scala正则的区别
Java调用的主体是字符串,Scala调用的主体是正则对象1.2 Java正则和Scala正则的的基本知识点
Java正则
public static void main(String[] args) { // 匹配 String regex = "\\d+"; String content1 = "123"; System.out.println(content1.matches(regex)); // true, 因为"123"完全由数字组成 // 替换 String content2 = "123,avc,$%#"; System.out.println(content2.replaceAll(regex, "")); // ",avc,$%#", 将所有数字替换为空字符串 System.out.println(content2.replaceFirst(regex, "")); // ",avc,$%#", 将第一个数字序列替换为空字符串 // 分割 String content3 = "123,asd,#$,456"; System.out.println(content3.split("[^0-9]+").toString()); // 打印的是数组对象的内存地址, 如"[Ljava.lang.String;@15db9742" // 分组 final Pattern cp1 = Pattern.compile("([a-z]+.*?\\d+)"); final Pattern cp2 = Pattern.compile("([a-z]+):(\\d+)"); String scores = "java:88,mysql:a99,hadoop:82,spark:91"; final Matcher mt1 = cp1.matcher(scores); while (mt1.find()){ final String score = mt1.group(0); final Matcher mt2 = cp2.matcher(score); if(mt2.find()){ System.out.println(mt2.group(1) + "->" + mt2.group(2)); // "java->88", "hadoop->82", "spark->91" } }}
Scala正则
// 初始化正则表达式val regexStr: String = "\\d+"val regex: Regex = regexStr.r// 字符串匹配val content = "123"println(content.matches(regexStr)) // 输出:true// 字符串分割val content4 = "123,87,xy,921"content4.split("[^0-9]+").foreach(println) // 输出分割后的数字:123, 87, 921// 字符串替换// 替换字符串中的第一个匹配项:val content3 = "123,87,xy,921"println(regex.replaceFirstIn(content3, "66")) // 输出:66,87,xy,921// 替换所有符合正则的对象,匹配到就替换,没有匹配到就不替换,返回类型是Option[String]println(regex.replaceSomeIn(content3, mat => Some((mat.group(0).toInt + 1).toString)))// 替换所有符合正则的对象,匹配到就替换,没有匹配到就不替换,返回类型是Stringprintln(regex.replaceAllIn(content3, mat => (mat.group(0).toInt + 1).toString))// 模式匹配val content = "123,456,789"val regex = "(\\d+),(\\d+),(\\d+)".rprintln(content match { case regex(a, b, c) => (a.toInt + 1, b.toInt + 1, c.toInt + 1)})// 如果content2符合regex2的模式,regex2(a,b,c,d)会提取四个字符串,并且存放到变量a,b,c,d中;匹配成功后,将这四个字符串变量转换为整数+1。// 分组val pat = "([a-zA-Z]+):(\\d+)".r // 边界(粗略提取整体)val patIn = "([a-zA-Z]+):(\\d+)".r // 正则构成(提取细则)val scores = "java:88,mysql:99,hadoop:82,spark:91";// 提取第一个符合正则的对象val opt: Option[String] = pat.findFirstIn(scores)println(opt.get) // java:88// 提取所有符合正则的对象val it: Regex.MatchIterator = pat.findAllIn(scores)println(it.mkString(",")) // 输出:java:88,mysql:99,hadoop:82,spark:91// 提取第一个符合正则的对象,且该对象可以按组匹配val mat: Option[Regex.Match] = patIn.findFirstMatchIn(scores)val str: String = mat.get.group(1)val str2: String = mat.get.group(2)println(str + ":" + str2)// 输出:java:88// 提取出所有符合正则且可按组匹配的对象构成一个迭代器val mats: Iterator[Regex.Match] = patIn.findAllMatchIn(scores)mats.map(m=>{ val subject: String = m.group(1) val score: String = m.group(2) (subject,score)}).foreach(println)// 依次输出:(java,88), (mysql,99), (hadoop,82), (spark,91)
1.3 练习
练习一:使用正则表达式解析日志
现有如下日志信息,请使用scala正则表达式解析如下信息:
日志级别
日期
请求URI
INFO 2016-07-25 requestURI:/c?app=0&p=1&did=18005472&industry=469&adid=31INFO 2016-07-26 requestURI:/c?app=0&p=2&did=18005473&industry=472&adid=31INFO 2016-07-27 requestURI:/c?app=0&p=1&did=18005474&industry=488&adid=32
方法一:使用findAllMatchIn
方法提取出所有符合正则且可按组匹配的对象构成一个迭代器,后通过map
方法将每一行中符合的部分提取出来构成元组。
val logs = "INFO 2016-07-25 requestURI:/c?app=0&p=1&did=18005472&industry=469&adid=31\n" + "INFO 2016-07-26 requestURI:/c?app=0&p=2&did=18005473&industry=472&adid=31\n" + "INFO 2016-07-27 requestURI:/c?app=0&p=1&did=18005474&industry=488&adid=32"val pat = "(INFO|WARN|ERROR) ([0-9]{4}-[0-9]{2}-[0-9]{2}) requestURI:(.*)".rval it = pat.findAllMatchIn(logs)it.map(m=>{ val level: String = m.group(1) val date: String = m.group(2) val uri: String = m.group(3) (level,date,uri)}).foreach(println)/* 输出:(INFO,2016-07-25,/c?app=0&p=1&did=18005472&industry=469&adid=31)(INFO,2016-07-26,/c?app=0&p=2&did=18005473&industry=472&adid=31)(INFO,2016-07-27,/c?app=0&p=1&did=18005474&industry=488&adid=32)*/
方法二:使用collect
方法在传入的偏函数中构造模式匹配,将匹配的部分提取出来构成元组
val regex = "(INFO|WARN|ERROR) (\\d{4}-\\d{2}-\\d{2}) requestURI:(.*)".rArray( "INFO 2016-07-25 requestURI:/c?app=0&p=1&did=18005472&industry=469&adid=31", "INFO 2016-07-26 requestURI:/c?app=0&p=2&did=18005473&industry=472&adid=31", "INFO 2016-07-27 requestURI:/c?app=0&p=1&did=18005474&industry=488&adid=32", "WRONG 2016-07-26 requestURI:/c?app=0&p=2&did=18005473&industry=472&adid=31").collect({ case regex(level,date,uri) => (level,date,uri)}).foreach(println)/* 输出:(INFO,2016-07-25,/c?app=0&p=1&did=18005472&industry=469&adid=31)(INFO,2016-07-26,/c?app=0&p=2&did=18005473&industry=472&adid=31)(INFO,2016-07-27,/c?app=0&p=1&did=18005474&industry=488&adid=32)*/
练习二:提取字符串的每组数据
val scores = "java:88,mysql:99,hadoop:82,spark:91"val regex: Regex = "([a-z]+):(\\d+)".rval it: Iterator[Regex.Match] = regex.findAllMatchIn(scores)it.foreach(mat => println(mat.group(1),mat.group(2).toInt))/*(java,88)(mysql,99)(hadoop,82)(spark,91)*/
val scores = "java:88,mysql:99,hadoop:82,spark:91"val pat1: Pattern = Pattern.compile("([a-z]+.*?\\d+)")val pat2: Pattern = Pattern.compile("([a-z]+):(\\d+)")val mat1: Matcher = pat1.matcher(scores)while(mat1.find()){ val score: String = mat1.group(0) val mat2: Matcher = pat2.matcher(score) if(mat2.find()){ println((mat2.group(1), mat2.group(2))) }}
二、隐式类
隐式类允许你向已存在的类型添加新的方法,是一种便捷的方式在不修改源代码的情况下扩展类的功能。
定义: 隐式类通常用于扩展某个类型的功能。它们通过隐式转换将原始类型转换为提供额外方法的新类型。 约束条件: 位置:隐式类必须定义在类、trait或者对象(包括单例对象和包对象)内部。构造器参数:隐式类的主构造器==必须且只能有一个非隐式参数。==这个参数是被扩展的类型,隐式类将为这个类型添加新的功能。非case类:==隐式类不能是case类。==Scala的case类用于模式匹配和简化数据类的创建,但隐式类用途不同,它主要用于类型转换和扩展功能。唯一性:在定义隐式类的同一作用域内,不能有与之同名的其他方法、成员或对象。// 字符串的方法扩展,而在 Java 中 String 是final的,无法扩展implicit class StrExt(str:String){ def incr() = str.map(c=>(c+1).toChar) def isEmail = str.matches("\\w+@[a-z0-9]{2,10}\\.(com(.cn)?|cn|edu|org)")}val a:String = "12665473@qq.com"val incr: String = a.incrval isEmail: Boolean = a.isEmail
三、异常
3.1.Java的基本异常处理结构
try-catch-finally
private static void close(AutoCloseable...acs){ for (AutoCloseable ac : acs) { if (Objects.nonNull(ac)) { try { ac.close(); } catch (Exception e) { e.printStackTrace(); } } }}try{ // 可能抛出异常的代码块 BufferedReader reader = new BufferedReader(new FileReader("test.txt")); System.out.println(reader.readLine());} catch(IOException ex) { // 异常的捕获和处理 System.err.println("An IOException occurred: " + ex.getMessage());} finally{ // 无论是否发生异常,都会执行的代码块,通常用于释放资源 close(reader);}
try-with-resources
try (BufferedWriter writer = new BufferedWriter(new FileWriter("test.txt"))) { // 使用资源,该资源必须extends AutoCloseable // try块中的`BufferedWriter`在代码块执行完毕后会自动关闭,不需要再通过finally关闭资源 writer.write("Hello World");} catch (IOException ex) { // 异常的捕获和处理 System.err.println("An IOException occurred: " + ex.getMessage());}
对比
try-catch-finally
中finally
不仅可以关闭资源,还可以用于执行其他代码块。
try-with-resources
资源会立即被关闭,InputStream,OutputStream,Reader,Writer等均实现了该接口。
try-with-resources
优先于try-catch-finally
,因为可能会存在finally延迟。
3.2.Scala的异常处理机制
3.2.1 异常数据类型
Option类型
使用Option
类型可以在函数无法返回有效结果时,返回None
,而不是抛出异常。
def divideOption(a:Int,b:Int):Option[Int] = { try { Some(a/b) }catch { case _ : ArithmeticException => None }}
Either类型
Either
类型通常用于函数可能返回两种类型的结果,其中Left
通常用于错误或异常,Right
用于正常值。
Either[String, Int]
需要包含两种数据类型的原因是需要同时兼容``Right[Nothing,Int]
和Left[String,Nothing]
的类型。
def divideEither(a: Int, b: Int): Either[String, Int] = { try { Right(a / b) }:Right[Nothing,Int] catch { case _: ArithmeticException => Left("divide by zero"):Left[String,Nothing] }}
Try类型
Try
是一个代表可能会成功或失败的操作的容器类型。Success
表示操作成功,Failure
表示操作失败。
Failure
需要一个Throwable
对象作为参数。
def divideTry(a: Int, b: Int): Try[Int] = { try { Success(a / b) } catch { case _: ArithmeticException => Failure(new Exception("divide by zero")) }}
值获取方式
Option
类型:getOrElse()
Either
和 Try
类型:模式匹配
val either: Either[Int, String] = Left(404)val errorInfo = either match { case Left(err) => s"发生错误,错误码:$err" case Right(value) => s"操作成功,结果为:$value"}// 这会返回:"发生错误,错误码:404"
import scala.util.{Try, Success, Failure}val attempt = Try { Integer.parseInt("abc") }val result = attempt match { case Success(value) => s"转换成功,结果为:$value" case Failure(ex) => s"转换失败,错误信息:${ex.getMessage}"}// 这会返回:"转换失败,错误信息:For input string: "abc""
3.2.2 模式匹配
catch的模式匹配
try { // 可能抛出异常的代码块 val result = 10 / 0} catch { // 捕获异常的格式是:case e:XxxException => ... case ex: ArithmeticException => println("ArithmeticException occurred") case ex: NullPointerException => println("NullPointerException occurred") case ex: Exception => println("Other exception occurred: " + ex.getMessage)}
3.3.Scala的异常控制工具
allCatch工具
opt
: 将异常转换为Option
withTry
: 将结果包装在Try
中either
: 将异常或结果包装在Either
中 import scala.util.control.Exception.allCatchdef divideOpt(a: Int, b: Int) = allCatch.opt(a / b)def divideWithTry(a: Int, b: Int) = allCatch.withTry(a / b)def divideEither(a: Int, b: Int) = allCatch.either(a / b)
failAsValue工具
failAsValue
允许你指定某种异常类型,并在捕获到该异常时返回一个默认值。 import scala.util.control.Exception.failAsValuedef divideFail(a: Int, b: Int) = failAsValue(classOf[ArithmeticException])(-1)(a / b)
四、类型信息处理
4.1 定义类和子类
case class Text(author: String, title: String, price: Float)class TextSon(level: String, override val author: String, override val title: String, override val price: Float)extends Text(author, title, price) { val _level: String = level override def toString() = s"TextSon{${super.toString}, ${_level}}"}
添加新的属性后如何重新toString()
方法?
override def toString() = s"TextSon{${super.toString}, ${_level}}"
4.2 实例化和使用
val obj: Text = new TextSon("TOP", "The Wild Roaring", "张培元", 86.32f)
4.3 类型判断和检查
进行类型判断,检查obj
是否是TextSon
类型的实例
val isIns: Boolean = obj.isInstanceOf[TextSon]
进行类型转换,需要通过allCatch.opt()
进行异常处理
val safeSon: Option[TextSon] = allCatch.opt(obj.asInstanceOf[TextSon])println(opt.getOrElse("转换失败"))