2012-10-08 45 views
1

Observer patterncallback是Java中廣泛使用的設計模式。但是,對匿名類實現回調接口是一件非常痛苦的事情,所以在Scala中通常會爲這些回調類實現引入隱式轉換。例如,對於Runnable接口隱式轉換:Scala:傳遞沒有參數的函數參數

implicit def func2Runnable[F](f: => F): Runnable = 
    new Runnable { 
    def run() { 
     f 
    } 
    } 

並假設有一些聽衆註冊表:

def addMyListener(m: Runnable) { 
    // mock function for test 
    for (i <- 1 to 2) { 
    m.run() 
    } 
} 

然後隱式轉換神奇壓實我的代碼:

addMyListener(println("Printed twice")) 

問題:

當我通過多行代碼塊addMyListener(),只有代碼的最後一行被傳遞給它:

addMyListener { 
    println("This line is printed once") 
    println("Only this is printed twice") 
} 

已知的解決方法:

予加在轉換功能的括號:

implicit def func2Runnable[F](f:() => F): Runnable = 
    new Runnable { 
    def run() { 
     f() 
    } 
    } 

但是,當我們使用它,它是冗長:

addMyListener(() => println("Printed twice")) 
addMyListener {() => 
    println("This line is printed twice") 
    println("This is also printed twice") 
} 

是否有一個更清晰的解決方案來揭露這個?

回答

2

好的,這裏有一些想法。我添加了第二個隱式轉換,但這次必須明確觸發。順便說一句,它只涉及解決方法之前的部分。

implicit def func2Runnable2(f: => Unit) = new { 
    def runnable = new Runnable { 
    def run() { 
     f 
    } 
    } 
} 

在REPL:

scala> addMyListener ({ 
    | println("This line is printed once") 
    | println("Only this is printed twice") 
    | }.runnable) 
This line is printed once 
Only this is printed twice 
This line is printed once 
Only this is printed twice 

所以,我的理論是,第一隱含的轉換在最後一行只發生。所以它會desugar到

addMyListener ({ 
    println("This line is printed once") 
    func2Runnable(println("Only this is printed twice")) 
}) 

,而不是預期的:

addMyListener(func2Runnable{ 
    println("This line is printed once") 
    println("Only this is printed twice") 
}) 

我們來測試兩個假設:

scala> addMyListener ({ 
    | println("This line is printed once") 
    | func2Runnable(println("Only this is printed twice")) 
    | }) 
This line is printed once 
Only this is printed twice 
Only this is printed twice 

scala> addMyListener(func2Runnable{ 
    | println("This line is printed once") 
    | println("Only this is printed twice") 
    | }) 
This line is printed once 
Only this is printed twice 
This line is printed once 
Only this is printed twice 

Yey!正如我想表明:)

對於更想在這個問題上,通知現在的說法是Runnable的本身名稱:

def addMyListener2(m: => Runnable) { 
    // mock function for test 
    for (i <- 1 to 2) { 
    m.run() 
    } 
} 

scala> addMyListener2 ({ 
    | println("This line is printed once") 
    | println("Only this is printed twice") 
    | }) 
This line is printed once 
Only this is printed twice 
This line is printed once 
Only this is printed twice 

投機所發生的事情:打印(...)他們都是單位。 {prints-inside}也是單位。所以它只是將func2Runnable的隱式轉換應用到塊的最後一個參數,而不是塊本身。

現在看到這個的正確方法是用scala -Xprint:namer(可能是typer而不是namer)開始REPL會話。

+0

好。感謝您的洞察,desugared隱式函數只應用於最後一行。但是,偵聽添加功能通常由第三方編寫,所以我們不能修改它;如果我可以修改它,我根本不會使用Runnable !. – pocorall

+0

所以有{...} .runnable,這在我的選擇中更安全,因爲它會阻止你在隱含的地方應用隱含的內容。在我的android應用程序中,我使用一系列促進方法來做類似的事情。 – pedrofurla

+0

嘖嘖,我錯過了。謝謝。 – pocorall

0

自我回答:

我發現這兩種類型的隱式轉換可以在同一時間被應用:

implicit def lazy2Runnable[F](f: => F): Runnable = 
    new Runnable { 
    def run() { 
     f 
    } 
    } 

implicit def func2Runnable[F](f:() => F): Runnable = 
    new Runnable { 
    def run() { 
     f() 
    } 
    } 

雖然當我們寫一個多行塊一點點應小心,導致代碼有點改進:

addMyListener(println("Printed twice")) 
addMyListener(() => { 
    println("This line is printed twice") 
    println("This is also printed twice") 
})