Scala School-1-基础(续)

http://twitter.github.io/scala_school/zh_cn/basics2.html

  • apply方法

apply方法给了类或者对象一个很方便的语法糖,我们在实例化对象时,看起来就像是在使用一个方法,比如:

scala> class Foo {}
defined class Foo

scala> object FooMaker {
     |   def apply() = new Foo // 伴生类中定义apply
     | }
defined module FooMaker

scala> val newFoo = FooMaker() // 不需要new了,直接使用半生对象中的方法就能实例化
newFoo: Foo = Foo@5b83f762

或者这样:

scala> class Bar {
     |   def apply() = 0 // 类中定义apply
     | }
defined class Bar

scala> val bar = new Bar
bar: Bar = Bar@47711479

scala> bar() // 可以直接使用,而不需要bar.apply
res8: Int = 0

apply是一种很方便的语法糖。

  • 单例对象

在刚才的例子中,注意到Foo和FooMaker是不一样的名字,因此,如果希望直接用:

Foo()

这是不可能的,只能用FooMaker()。那如果希望更方便的话,将单例对象直接命名为类的名字就可以了。此时,单例对象也叫做该类的伴生对象。

class Foo {}
object Foo{
  def apply()=new Foo
}
Foo()

伴生对象

  • 函数即对象
    这里的extend表明了继承关系,继承了一个叫做Fuction1[Int, Int]的东西。
scala> object addOne extends Function1[Int, Int] {
     |   def apply(m: Int): Int = m + 1
     | }
defined object addOne

scala> addOne(1)
res2: Int = 2

这个Function1是一个trait,它有一个很方便的apply方法,如上所述。

那么同样的,还可以这么做:

case class TestFunc1() extends Function1[Int, Int]{
  def apply(i: Int): Int = i
}
scala> TestFunc1
res45: TestFunc1.type = TestFunc1
scala> TestFunc1()
res46: TestFunc1 = <function1>
scala> TestFunc1()(11)
res47: Int = 11

这里就要来解释一下到底这是在干什么了。我们还是以数学的方式比较。我们先看一下trait Function1是什么。

trait Function1[-T, +R] extends AnyRef { self =>
  def apply(v1: T1): R

  ...
}

这个特质,表达的是一种单参数函数关系。就如同一个数学函数,

Anything => Anything

表达了一种对应关系,这是一种一元对应关系,比如:
y = x + 1, 一元一次函数;
y = x^2 + x + 1,一元二次函数;
这都是一元对应关系。
同时,因为这种关系并没有对定义域和值域有任何要求,表达的是一种抽象关系,于是我们用trait来表达。同时应该记得,trait也是用来给类增加一些特性的,比如之前说的,很多动物都能发声。Function1提供了一个特性是apply()。它的好处是,可以直接使用SomeClass(input)。
这个特性反映在数学上,就是针对这种抽象的对应,如果我们希望将其落实到具体的函数上怎么办?落实,也就是将trait的抽象,反映到具体的类/对象上。
trait有两个部分是可以具体化的。
首先,Function1使用的是泛型,这个类型首先可以具体化。数学表达就是

Int => Int

我们希望得到抽象结构的子集,只考虑Int的情况。这个的解决办法是

//数学上依然抽象的,这样的情况就是没指定类型的Function1;类形上有指定,为泛型
abstract class TestG[K, V] extends Function1[K, V]
//数学上抽象,因为没有定义函数本身
abstract class TestAbs extends Function1[Int, Int]
trait TestAbsT extends Function1[Int, Int]

为什么这样?我们不能使用case class/class,因为Function1还有另外一个抽象的部分,apply方法。只有这个方法具体化了,我们才能将其实例化。反映到数学上,它的表达就是

Int => Int
y = x^2 + 2x + 1

我们希望得到Int函数的子集,一个具体的一元二次函数。
这个很具体了,我们于是可以

case class TestIntFunc() extends TestAbs {
  def apply(i: Int): Int = i*i + 2 * i + 1
}
case class TestBi() extends Function1[Int, Int] {
  def apply(x: Int): Int = x*x + 2 * x + 1
}

这样,我们就得到了一个定义域/值域为[Int, Int],且具体的一元二次函数。小结一下这些概念的对应。

定义域/值域不固定,抽象函数,Function1
定义域/值域固定,抽象函数,继承自Function1的trait/abstract
定义域/值域固定,具体函数,class/case class

这里还有一个很容易混淆的东西,apply。
我们知道case class是有伴随的object,定义了很方便的apply,让其可以当作CaseClass(item),类似函数一样使用,直接得到某个实例,
而Trait也定义了一个apply。这两个apply有什么区别?联系?

class TestBiClass extends Function1[Int, Int] {
  def apply(x: Int): Int = x*x + 2 * x + 1
}

case class的apply,是帮助其实例化从而建立一个新的obj,即省略了new CaseClass这一步的;
Function1的apply,是帮助函数自动运行某种计算的,即省略了NewedClass.apply()而直接使用NewedClass()的。
所以apply这个方法是一个特殊的方法,所有的类都可以建立自己的apply方法,从而省略.apply而直接使用SomeClass()。

class TestApply{
  def apply(x: Int): Int = x*x + 2 * x + 1
}

当apply作用于case class时,这种省略是为了节约new这个步骤,直接得到obj。
当apply作用于class,尤其是某种函数形成的class时,这种省略是为了进行某种运算,直接得到结果。
同时应该知道,case class的apply,位于其伴随obj中,是自动生成的。class的apply则位于类的内部。那如果我们给class也加一个伴随obj,并添加apply方法,是不是就可以和case class一样了?当然!见前文的单例对象/伴生对象。
接下来,我们从一个class到instance来看看发生了什么。

class TestBiClass extends Function1[Int, Int] {
  def apply(x: Int): Int = x*x + 2 * x + 1
}
//这样是不行的,因为没有伴生对象
scala> TestBiClass
<console>:25: error: not found: value TestBiClass
       TestBiClass
//生成了一个TestBiClass实例
scala> val tb = new TestBiClass
//这个实例是一个类型为Function1的函数。如果从对象角度理解,这是一个类型为TestBiClass的实例对象,它有一个方法为apply。
res69: TestBiClass = <function1>
//我们可以使用apply方法,就如所有其他的class的对象一样。实例对象使用类的方法。
scala> tb.apply(2)
res70: Int = 9
//但因为apply方法的特性,我们可以省略掉它,直接使用
scala> tb(2)
res71: Int = 9
//单纯从函数的角度理解,tb是一个函数,函数如何使用?f(x) = x^2+2x+1,f函数构建在定义域x之上。
//那么要使用它,就是f(x)。TestBiClass是一个函数,定义在x这个整数类型之上,要用它就是TestBiClass(x)。
//这就是另外一层含义了。
scala> tb(2)
res71: Int = 9

同样的例子也可以用在case class身上。

case class TestBi() extends Function1[Int, Int] {
  def apply(x: Int): Int = x*x + 2 * x + 1
}
//告诉我们TestBi是一个类(型)
scala> TestBi
res72: TestBi.type = TestBi
//case class自带的apply帮我们完成了实例化,我们得到了一个TestBi实例。这个实例是一个Function1[Int, Int]类型的函数
//理论上来说已经精确到Function1[Int, Int]上了。Function1[T, R]算是泛型的trait,这里具体化了。
scala> TestBi()
res73: TestBi = <function1>
//这是一个类型为TestBi的类的实例化对象,因此我们可以使用该类的方法,有继承自Function1的apply方法。
scala> TestBi().apply(2)
res74: Int = 9
//同样,因为apply方法的特性,我们可以省略掉它,直接使用
scala> TestBi()(2)
res75: Int = 9
//这里关于对象和函数的具体解释和上面的例子是一样的。
//如果我们查看一下TestBi()这个实例,我们就知道其真实的父类是什么
scala> TestBi().isInstanceOf[Function1[Int, Int]]
res78: Boolean = true
scala> TestBi().isInstanceOf[TestBi]
res79: Boolean = true
  • 异常

这里可以看到,首先try-catch-finally,这个结构是面向表达式的,也就是说,它本身是有一个结果的,在这里是Int。
同时,finally在最后被调用,且不是表达式的一部分,也就是说其结果并不会做为值被传递return。

val result: Int = try {
  remoteCalculatorService.add(1, 2)
} catch {
  case e: ServerIsDownException => {
    log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.")
    0
  }
} finally {
  remoteCalculatorService.close()
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,764评论 0 38
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 变量初始化可以用用 _ 作占位符,赋值为默认值,字符串 null,Float、Int、Double 等为 0var...
    FaDeo_O阅读 903评论 0 0
  • 长久以来,从中草药,到牲畜家禽,再到水产养殖,人们都有一个很深的认知误区:野生的一定是最好,人工养殖的一定是差的。...
    橙君阅读 640评论 0 1
  • 从进入科院到现在,我感觉我对大学的那种新奇劲和冲劲已经被我的拖延症磨灭,感觉每天都是被身体拖着走,而不是自己刚进大...
    胡志勇_三月阅读 273评论 1 2