對scala的隐式參數又愛又恨,既強大得讓人歡呼雀躍,但繁瑣的機制與晦澀的文法又讓人望而卻步,總結起來,implicit一共有三種用法,一種比一種強大,這裡就不講太深奧的理論,直接進入應用場景實戰。
1. 預設參數值
首先,需要注意的是,在方法參數上添加implicit關鍵字對方法幾乎沒有任何影響,原來的調用方式依舊。
其次,implicit作為參數聲明時,隻能作為方法的唯一參數,是的,你沒看錯,唯一的參數,還有每個方法也隻能聲明一個參數!如果混在一起,那麼它幾乎沒有作用,因為每一次調用時你都必須指定方法的所有參數。當然,scala提供了完美的解決辦法,請看下面的例子:
test("隐式參數必須獨立聲明") {
// 調用時無法省略參數
def add(implicit a: Int, b: Int): Int = a + b
// 一定要作為最後一個參數
def plus(a: Int)(implicit b: Int): Int = a + b
// 聲明隐式參數,不需要名稱相同,隻能類型相同就可以了
implicit val c: Int =
// 單元測試
assertResult(plus())()
}
從上面的例子可以得出如下結論:
1. 通過高階函數的方法,隐式參數可以和其他參數一起合作,但受限于調用方式,必須作為函數的最後一個參數;
2. 聲明隐式參數時,并不需要采用與函數參數一樣的名稱,隻要類型相同即可;
2. 參數類型轉換
在方法的調用中,實參的類型有時難以比對形參的類型,此時需要可以利用隐式方法進行自動轉換
test("隐式方法轉換類型") {
// 調用的方法,并不需要聲明為隐式參數
def plus(a: Int, b: Int): Int = a + b
// 定義轉換函數
implicit def stringToInt(s: String): Int = Integer.parseInt(s)
assertResult(plus("3", "5"))()
}
從上面的例子中,我們可以看出:
1. 調用的方法不需要攜帶隐式參數,适用于所有的方法;
2. 定義轉換函數後,大大增強了方法參數的适配性;
3. 隐式函數查找的作用域跟隐式參數的作用域一模一樣;
3. 動态添加屬性與方法
除了可以批量指派預設參數,以及動态轉換函數外,implicit最強大的地方還在于能為每種類型動态添加屬性與方法,如下:
test("動态添加屬性與方法") {
// 參數就是添加屬性與方法的類型
implicit def strFile(dir: String) = new {
// 動态添加屬性
val isDir:Boolean = true
// 動态添加方法
def listDir(): List[File] = {
val d = new File(dir)
// _隻有作為參數時才能省略,作為主體時不能省略
d.listFiles().filter(_.isDirectory).toList
}
}
assert("/".listDir().size > )
assert("/".isDir)
}
這個功能簡直強大到吓人,任意類型一律通殺,無視類型的聲明是否是不變模型(final),無視類型是自定義還是系統内置。有了這樣的機制,寫出一萬公裡長的鍊式代碼也是易如反掌。
4. 隐式作用域
為了找到相關的隐式參數與方法,scala會按如下優先級查找相關的隐式聲明:
1. 在目前函數調用的作用域及父作用域内;
2. 在聲明隐式參數類型的伴生作用域内;
結論
簡簡單單的implicit關鍵字,對Scala的功能增強幾乎是天翻地覆,隻有了解了implicit,尤其是第三種用法,才能了解如同天書一樣的DSL代碼(尤其是初次使用的JAVA程式員),強烈推薦《深入了解Scala》(這本書翻譯得不太好,但内容很有深度)進一步閱讀。