美文网首页
scala apply详解

scala apply详解

作者: tracy_668 | 来源:发表于2021-07-21 09:19 被阅读0次

    [TOC]
    关于scala中的特殊方法apply,先给一个比较通俗的解释。

    当对象(伴生对象)以函数的方式进行调用时,scala 会隐式地将调用改为在该对象上调用apply方法。

    例如: Demo(“hello”) 实际调用的是 Demo.apply(“hello”), 因此apply方法又被称为注入方法。

    apply方法常用于创建类实例的工厂方法。

    case class SomeBean(name: String, age: Int)
    

    对于这个 case 类会生成一个 SomeBean 的伴生对象 SomeBean,这个伴生对象中定义了 apply 与 unapply 方法。

    下面这段代码是反编译后得到的

    public Option<Tuple2<String, Object>> unapply(SomeBean x$0) {
      return x$0 == null ? None..MODULE$ : new Some(new Tuple2(x$0.name(), BoxesRunTime.boxToInteger(x$0.age())));
    }
    
    public SomeBean apply(String name, int age) {
      return new SomeBean(name, age);
    }
    

    apply

    将对象以函数的方式进行调用时,scala会隐式地将调用改为在该对象上调用apply方法。例如XXX(“hello”)实际调用的是XXX.apply(“hello”), 因此apply方法又被称为注入方法。apply方法常用于创建类实例的工厂方法。

    定义在 object 中

    object Greeting{
      def apply(name: String) = "Hello " + name
    }
    

    Greeting.apply(“Lucy”) //与下面的调用等价
    Greeting(“Lucy”) //结果为 Hello Lucy

    定义在 class 或 trait 中
    首先我们来看下 scala 集合类 List 对 apply 的使用。scala 中 List 在读取数据时与 java 的 List 不同。
    java 中使用 list.get(index) 取得第 index 个索引的数据,但在 scala 中只需要使用 list(index )就可以实现。
    这个功能就是借助于 apply 方法实现的。例如:

    List(1, 2, 3)
    List("a", "b", "c")
    

    或者是Map也有类似的用法:

    Map(1 -> "a", 2 -> "b", 3 -> "c")
    

    以上的两段代码并不是在调用 List 和 Map 的 constructor,而是在调用 List 和 Map 的伴生对象(companion objects)的 apply 方法。

    下面我们来实际使用 apply 方法来显示获取特定位置的字符的需求(类似与模拟 Map 和 List 中的取值方法)

    class IndexedString(val str: String) {
      def apply(charIndex: Int) = str.charAt(charIndex)
    }
    

    我们可以通过 IndexedString 的 apply 方法可以通过索引读取在字符中对应的字符

    val indexed = new IndexedString("Hello world")
    indexed(0) //结果为H, 等价于indexed.apply(0)
    

    scala 的函数对象

    scala 中的函数对象(头等函数或first-class function)是使用 apply 的典型用法:scala 中的函数对象是scala.FunctionN 特质的实例,使用时函数对象直接调用 FunctionN 的 apply 方法。

    scala> val increment: (Int) => Int = _ + 1 
    increment: Int => Int = <function1>
    // 调用 scala.FunctionN的apply方法
    scala> increment(0)
    res0: Int = 1
    

    unapply

    与 apply 相对的是 unapply 方法,它的用法与 apply 类似,但其作用是用来抽取部分参数,它也称为抽取方法,主要用于模式匹配时抽取某些参数 case XXX(str) => println(str)

    Scala比Java更面向对象的一个方面是Scala没有静态成员。替代品是,Scala有单例对象:singleton object。

    当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。

    类和单例对象间的一个差别是,单例对象不带参数,而类可以。因为你不能用new关键字实例化一个单例对象,你没机会传递给它参数。每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现,因此它们与Java静态类有着相同的初始化语法。Scala程序特别要指出的是,单例对象会在第一次被访问的时候初始化。

    Scala 的apply 有2 张形式,一种是 伴生对象的apply ,一种是 伴生类中的apply,下面展示这2中的apply的使用。

    class ApplyOperation {
    }
    class ApplyTest{
      def apply(b:String) = println(b+":I am into spark so much!!!")
      def haveATry: Unit ={
        println("have a try on apply")
      }
    }
    object ApplyTest{
      def apply(i:Int) = {
        println(i.toString+":I  am into Scala so much")
        new ApplyTest
      }
    }
    object ApplyOperation{
      def main (args: Array[String]) {
        val array= Array(1,2,3,4)
        val a = ApplyTest(111) //这里就是使用object 的使用
        val b= ApplyTest.apply(222)
    
        a.haveATry
        a("AAA") // 这里就是 class 中 apply使用
        b.apply("BBB")
        new ApplyTest
        new ApplyTest()
        (new ApplyTest).apply("CCC")
        // ApplyTest("DDD")--error   object的apply方法参数是Int类型
        //new ApplyTest(555)--error  class的apply方法不是构造方法
        ApplyTest(333)
        ApplyTest.apply(444)
      }
    }
    

    Scala<apply的几种用法>

    1.只是一个快捷方式
    首先定义个object:

    object Greet {
      def apply(name: String): Unit = {
        println("Call From %s".format(name))
      }
    }
    
    object Main {
      def main(args: Array[String]): Unit = {
        Greet.apply("Gpwner")
        println("==========================")
        Greet("pwner")
      }
    }
    

    所以当调用一个Object的时候,其实就是相当于调用了这个Object的apply方法

    但是apply并不等同与构造函数

    image.png
    2.scala会自动为case class 生成apply方法

    下面的三个操作都是一样的结果:

    val p0 = new Person("Frank", 23, "Blue") // normal constructor
     
    val p1 = Person("Frank", 23, "Blue") // this uses apply
     
    val p2 = Person.apply("Frank", 23, "Blue") // using apply manually
    
    3、apply应用于builder模式

    一个object中的apply方法不一定要返回其自身对象,比如

    case class Company(name: String)
     
    class Person(val name: String) {}
     
    object Person {
      def apply(name: String): Company = new Company(name)
    }
    

    利用这个特点我们可以将一个类写成Builder

    abstract class DatabaseDriver {
      // some database stuff
    }
     
    object DatabaseDriver {
      def apply(config: Configuration) = config.dbType match {
        case "MYSQL" => new MySqlDriver()
        case "PSQL" => new PostgresDriver()
        case _ => new GenericDriver()
      }
    }
     
    // now I get the right version!
    val mydatabase = DatabaseDriver(dbConfig)
    
    5、在class中的apply

    可以在object中有apply方法,当然也在class中定义apply方法

    class Amazing {
      def apply(x: String) = "Amazing %s!".format(x)
    }
     
    // look how cool this is
    val amazing = new Amazing()
    amazing("world")
    // => Amazing world!
    amazing.apply("world")
    // => Amazing world!
    

    相关文章

      网友评论

          本文标题:scala apply详解

          本文链接:https://www.haomeiwen.com/subject/nyndmltx.html