zoukankan      html  css  js  c++  java
  • Scala学习笔记--提取器unapply

    提取器就是一个带有unapply方法的对象。你可以把unapply方法当做是伴生对象中apply方法的反向操作。

    apply方法接收构造参数,然后将他们变成对象。

    而unapply方法接受一个对象,然后从中取值--通常这些值就是当初用来构造该对象的值。

    转自崔鹏飞的博客  博文地址:http://blog.csdn.net/cuipengfei1/article/details/33353159

    实在想不到什么动词可以当做脱衣服来讲了,所以从现在开始这系列博文就叫做Desugar Scala了。除非哪天才思泉涌,又想到了新词:)

    开始正文。

    名字叫做unapply和unapplySeq的方法在Scala里也是有特殊含义的。

    我们前面说过case class在做pattern match时很好用,而除case class之外,有unapply或unapplySeq方法的对象在pattern match时也有很好的应用场景。

    比如这段代码:

    1
    2
    3
    
    object Square {
      def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
    }
    

    我们定义了一个unapply方法,用来计算平方根。 我们可以像调用普通方法一样的调用它:

    1
    2
    
    val number: Double = 36.0
    Square.unapply(number)
    

    这样会得到36的平方根:6。实际上返回值是Some(6)。

    上面的方式是对unapply的浪费,unapply真正的好处是这样的:

    1
    2
    3
    4
    5
    
    val number: Double = 36.0
    number match {
      case Square(n) => println(s"square root of $number is $n")
      case _ => println("nothing matched")
    }
    

    这样我们无需显式调用unapply方法,而把是它用在pattern match中,让编译器替我们调用它。

    当我们写下这段pattern match的代码时,编译器其实替我们做了好几件事:

    1. 调用unapply,传入number
    2. 接收返回值并判断返回值是None,还是Some
    3. 如果是Some,则将其解开,并将其中的值赋值给n(就是case Square(n)中的n)

    这段代码反编译出来是这个样子的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
      double number = 36.0D;
      double d1 = number;
      Option localOption = Square..MODULE$.unapply(d1);
      //调用unapply,传入number
      BoxedUnit localBoxedUnit;
      if (localOption.isEmpty()) {//判断返回值是None
        Predef..MODULE$.println("nothing matched");
        localBoxedUnit = BoxedUnit.UNIT;
      }
      else {//判断返回值是Some
        double n = BoxesRunTime.unboxToDouble(localOption.get());
        //将Some解开,并将其中的值赋值给n
        Predef..MODULE$.println(new StringContext(Predef..MODULE$.wrapRefArray((Object[]) new String[] {
          "square root of ", " is ", ""
        }) ).s(Predef..MODULE$.genericWrapArray(new Object[] {
          BoxesRunTime.boxToDouble(number), BoxesRunTime.boxToDouble(n)
        })));
        localBoxedUnit = BoxedUnit.UNIT;
      }
    

    如果没有unapply方法和pattern match语法之间的这种结合,我们自己写代码要写成什么样子呢?

    或许会比上面反编译的代码简单一些,但是显式地调用开平方的方法,用if else来判断Option,以及将真正的返回值从Option里面解出来这三件事是免不掉的。

    unapplySeq和unapply的作用很是类似,例如这样:

    1
    2
    3
    4
    5
    6
    
    object Names {
      def unapplySeq(str: String): Option[Seq[String]] = {
        if (str.contains(",")) Some(str.split(","))
        else None
      }
    }
    

    我们定义一个unapplySeq方法,用逗号作为分隔符来把字符串拆开。

    然后我们可以这样应用它:

    1
    2
    3
    4
    5
    6
    7
    8
    
    val namesString = "xiao ming,xiao hong,tom"
    namesString match {
      case Names(first, second, third) => {
        println("the string contains three people's names")
        println(s"$first $second $third")
      }
      case _ => println("nothing matched")
    }
    

    另一个unapplySeq例子

    object Doman{
      
      def apply(parts : String*):String ={
        parts.reverse.mkString(".");
      }
      
      def unapplySeq(str:String):Option[Seq[String]] = {
        Some(str.split("\.").reverse);
      }
      
      def myMatch(str:String){
        str match{
        	case Doman("org","acm") => println("acm.org");
        	case Doman("com","sun","java") => println("java.sun.com");
        	case Doman("net",_*) =>println("a .net doman")
        }
      }
      
      def main(args : Array[String]):Unit = {
        val str1 = "acm.org";
        val str2 = "java.sun.com";
        val str3 = "a.b.c.net";
        myMatch(str1);
        myMatch(str2);
        myMatch(str3);
      }
    }
    

    输出结果:

    acm.org
    java.sun.com
    a .net doman

    与上面的例子很是类似,不过编译器在这里替我们做的事情更多了:

    1. 调用unapplySeq,传入namesString
    2. 接收返回值并判断返回值是None,还是Some
    3. 如果是Some,则将其解开
    4. 判断解开之后得到的sequence中的元素的个数是否是三个
    5. 如果是三个,则把三个元素分别取出,赋值给first,second和third

    如果没有unapplySeq方法和pattern match语法之间的这种结合,我们自己写代码来做这五件事会显得很是繁琐。

  • 相关阅读:
    字节流与字符流,字节流和字符流的使用哪个多?
    java 读写操作大文件 BufferedReader和RandomAccessFile
    hibernate官网文档
    阿里云centos怎么用xshell5登陆
    STM8L使用外部8M HSE
    vscode函数注释添加【转载】
    某个通信的异常判断存在于两个任务中时计算通信超时的一种思路
    STM32F0芯片读保护
    FrameworkCubeMX.gpdsc missing的问题
    git忽略已经提交的文件【转载】
  • 原文地址:https://www.cnblogs.com/gnivor/p/4267347.html
Copyright © 2011-2022 走看看