簡體   English   中英

為什么Scala不會在這里使用隱式轉換?

[英]Why won't Scala use implicit conversion here?

我想打電話給該set的方法記錄在這里 ,Java庫jOOQ ,有簽名:

<T> ... set(Field<T> field, T value)

這個Scala系列是一個問題:

 .set(table.MODIFIED_BY, userId)  

MODIFIED_BY是表示表列的Field<Integer> userIdInt Predef有一個從IntInteger的隱式轉換,那么它為什么不使用呢? 我明白了:

type mismatch; found: org.jooq.TableField[gen.tables.records.DocRecord,Integer]    
            required: org.jooq.Field[Any] 
Note: Integer <: Any 
(and org.jooq.TableField[gen.tables.records.DocRecord,Integer] <: 
     org.jooq.Field[Integer]), but Java-defined trait Field is invariant in type T. 
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)

更新 - 關於Vinicius的例子

這里不是試圖在注釋中解釋這一點,而是演示當你使用帶有協變參數的類型時沒有調用隱式轉換,比如List[+T] 假設我把這段代碼放在一個文件中,編譯並運行它......

case class Foo(str: String)

object StackOver1 extends App {

  implicit def str2Foo(s: String): Foo = { 
    println("In str2Foo.")
    new Foo(s)
  }

  def test[T](xs: List[T], x: T): List[T] = {
    println("test " + x.getClass)
    xs
  }

  val foo1 = new Foo("foo1")
  test(List(foo1), "abc")
}

您將看到它調用test,但從未將String “abc”隱式轉換為Foo 相反,它為test[T]選擇一個T ,它是StringFoo之間的公共基類。 當你使用IntInteger它選擇Any ,但是它很混亂,因為列表中Int的運行時表示是Integer 所以看起來它使用了隱式轉換,但事實並非如此。 您可以通過打開Scala提示進行驗證...

scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2)
List[Any]

我對jOOQ一無所知,但我認為問題在於Scala不能很好地理解java泛型。 嘗試:

scala> def test[T](a : java.util.ArrayList[T], b: T) = {  println(a,b) }
scala> val a = new java.util.ArrayList[Integer]()
scala> val b = 12
scala> test(a,b)
<console>:11: error: type mismatch;
 found   : java.util.ArrayList[Integer]
 required: java.util.ArrayList[Any]
Note: Integer <: Any, but Java-defined class ArrayList is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
              test(a,b)

聽起來很熟悉??

要修復,只需通知類型T調用方法: test[Integer](a,b)工作正常。

編輯:

這里涉及到一些事情:

  1. 擦除 - >編譯時,通用的類型將通過擦除消失。 編譯器將使用Scala將被視為Any的Object。 但是,ArrayList [Integer]不是ArrayList [Any],即使Integer是any。 與TableField [gen.tables.records.DocRecord,Integer]相同的方式不是Field [Any]。

  2. 類型推斷機制 - >它將確定T應該是什么類型,並且這樣做它將使用傳遞的類型的交集支配者(在我們的例子中是第一個共同的祖先)。 Scala語言規范的第36頁,在我們上面的例子中將使用Any。

  3. 隱式轉換 - >它是最后一步,如果有某種類型要轉換為另一種類型,則會被調用,但由於參數的類型被確定為第一個共同的祖先,因此無需轉換,我們將如果我們不強制類型T,永遠不會有隱式轉換。

顯示共同祖先如何用於確定T的示例:

scala> def test[T](a: T, b: T): T = a
scala> class Foo
scala> class Boo extends Foo
scala> test(new Boo,new Foo)
res2: Foo = Boo@139c2a6
scala> test(new Boo,new Boo)
res3: Boo = Boo@141c803
scala> class Coo extends Foo
scala> test(new Boo,new Coo)
res4: Foo = Boo@aafc83
scala> test(new Boo,"qsasad")
res5: Object = Boo@16989d8

總而言之,隱式方法不會被調用,因為類型推斷機制在獲取參數之前確定類型,並且因為它使用共同的祖先,所以不需要隱式轉換。

您的代碼由於擦除機制而產生錯誤,該機制隨類型信息消失,這對於確定參數的正確類型很重要。

@RobN,感謝您質疑我的回答,我從這個過程中學到了很多東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM