從"hello".count想到的之二--scala隱式轉(zhuǎn)換優(yōu)化級(jí)

string隱式轉(zhuǎn)換的二義性問題

scala標(biāo)準(zhǔn)庫(kù)在Predef對(duì)象中定義了兩個(gè)String的隱式轉(zhuǎn)換:

implicit def augmentString(x: String): StringOps
implicit def wrapString(s: String): WrappedString

StringOpsWrappedString有一些重復(fù)的方法横腿,如count
?StringOps中定義了count方法

def count(p: (Char) ? Boolean): Int
Counts the number of elements in the traversable or iterator which satisfy a predicate.

WrappedString也有count方法

def count(p: (Char) ? Boolean): Int
Counts the number of elements in the traversable or iterator which satisfy a predicate.

?兩個(gè)count方法??完全一樣,應(yīng)該存在二義性問題啊败许。試著在REPL中寫了?一?段隱式轉(zhuǎn)換的代碼越平,果然會(huì)提示有二義性:

case class A1(a: Int) {
  def guess = a * 10
}

case class A2(a: Int) {
  def guess = a * 100
}       

implicit def Int2A1(a: Int) = new A1(a)
implicit def Int2A2(a: Int) = new A2(a)

scala> 1.guess
<console>:14: error: type mismatch;
 found   : Int(1)
 required: ?{def guess: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method Int2A1 of type (a: Int)A1
 and method Int2A2 of type (a: Int)A2
 are possible conversion functions from Int(1) to ?{def guess: ?}
       1.guess
       ^
<console>:14: error: value guess is not a member of Int
       1.guess

但是從上一篇隱式轉(zhuǎn)換的文章可以知道"hello".count不但沒有報(bào)錯(cuò)闸拿,還會(huì)選擇StringOps.count褐桌。?為什么會(huì)這樣呢?

?隱式轉(zhuǎn)換的優(yōu)化級(jí)

Martin Odersky親自寫的《Programming in Scala(Third Edition)》21.7節(jié)最后有下面這一段說明:

The old implicit conversion to a Scala collection (now named WrappedString) is retained. However, there is a more specific conversion supplied fromString to a new type called StringOps. StringOps has many methods such as reverse, but instead of returning a collection, they return a String. The conversion to StringOps is defined directly in Predef, whereas the conversion to a Scala collection is defined in a new class, LowPriorityImplicits, which is extended by Predef. Whenever a choice exists between these two conversions, the compiler chooses the conversion to StringOps, because it's defined in a subclass of the class where the other conversion is defined.

簡(jiǎn)而言之饼记,編譯器之所以會(huì)選擇StringOps而不是WrappedString香伴,是因?yàn)?code>StringOps更特化(more specific)。為什么說StringOps更特化呢具则?讓?我們先看看Predef對(duì)象的?繼承關(guān)系:

object Predef extends LowPriorityImplicits with DeprecatedPredef {
  /* ??忽略了很多?東東... */
  /** @group conversions-string */
  @inline implicit def augmentString(x: String): StringOps = new StringOps(x)
}
private[scala] abstract class LowPriorityImplicits {
  /* ??忽略了很多?東東... */
  /** @group conversions-string */
  implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null
}

StringStringOps的隱式轉(zhuǎn)換是定義在Predef對(duì)象中的即纲,而StringWrappedString的?隱式轉(zhuǎn)換是在定義在Predef的???父類LowPriorityImplicits中,所以前者比后者更特化博肋。
?還是在《Programming in Scala(Third Edition)》21.7節(jié)低斋,有一??段更詳細(xì)的說明:

one implicit conversion is more specific than another if one of the following applies:

  • The argument type of the former is a subtype of the latter's.
  • Both conversions are methods, and the enclosing class of the former extends the enclosing class of the latter.

Odersky?又解釋道:

The motivation to revisit this issue and revise the rule was to improve interoperation between Java collections, Scala collections, and strings.

又試著在REPL寫了一段測(cè)試代碼,的確如此:

case class A1(a: Int) {
  def guess = a * 10
  def what = a
}

case class A2(a: Int) {
  def guess = a * 100
}       

class BaseImplicits {
  implicit def Int2A1(a: Int) = new A1(a)
}

object SpecificImplicits extends BaseImplicits {
  implicit def Int2A2(a: Int) = new A2(a)
} 

scala> import SpecificImplicits._
import SpecificImplicits._

scala> 1.guess
res1: Int = 100

scala> 1.what
res2: Int = 1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匪凡,一起剝皮案震驚了整個(gè)濱河市膊畴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌病游,老刑警劉巖巴比,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡轻绞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門佣耐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來政勃,“玉大人,你說我怎么就攤上這事兼砖〖樵叮” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵讽挟,是天一觀的道長(zhǎng)懒叛。 經(jīng)常有香客問我,道長(zhǎng)耽梅,這世上最難降的妖魔是什么薛窥? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮眼姐,結(jié)果婚禮上诅迷,老公的妹妹穿的比我還像新娘。我一直安慰自己众旗,他們只是感情好罢杉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贡歧,像睡著了一般滩租。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上利朵,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天律想,我揣著相機(jī)與錄音,去河邊找鬼哗咆。 笑死蜘欲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晌柬。 我是一名探鬼主播姥份,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼年碘!你這毒婦竟也來了澈歉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤屿衅,失蹤者是張志新(化名)和其女友劉穎埃难,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涡尘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年忍弛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片考抄。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡细疚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出川梅,到底是詐尸還是另有隱情疯兼,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布贫途,位于F島的核電站吧彪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丢早。R本人自食惡果不足惜姨裸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望香拉。 院中可真熱鬧啦扬,春花似錦、人聲如沸凫碌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盛险。三九已至瞄摊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苦掘,已是汗流浹背换帜。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹤啡,地道東北人惯驼。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像递瑰,于是被迫代替她去往敵國(guó)和親祟牲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容