简体   繁体   English

为什么Scala不会在这里使用隐式转换?

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

I'm trying to call this set method documented here , in the Java library jOOQ , with signature: 我想打电话给该set的方法记录在这里 ,Java库jOOQ ,有签名:

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

This Scala line is a problem: 这个Scala系列是一个问题:

 .set(table.MODIFIED_BY, userId)  

MODIFIED_BY is a Field<Integer> representing the table column. MODIFIED_BY是表示表列的Field<Integer> userId is Int . userIdInt Predef has an implicit conversion from Int to Integer , so why doesn't it use it? Predef有一个从IntInteger的隐式转换,那么它为什么不使用呢? I get this: 我明白了:

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)

Update - About Vinicius's Example 更新 - 关于Vinicius的例子

Rather than try to explain this in comments, here is a demonstration that there is no implicit conversion being called when you use a type with covariant parameter, like List[+T] . 这里不是试图在注释中解释这一点,而是演示当你使用带有协变参数的类型时没有调用隐式转换,比如List[+T] Let's say I put this code in a file, compile, and run it... 假设我把这段代码放在一个文件中,编译并运行它......

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")
}

You'll see that it calls test, but never the implicit conversion from String "abc" to Foo . 您将看到它调用test,但从未将String “abc”隐式转换为Foo Instead it's picking a T for test[T] that is a common base class between String and Foo . 相反,它为test[T]选择一个T ,它是StringFoo之间的公共基类。 When you use Int and Integer it picks Any , but it's confusing because the runtime representation of the Int in the list is Integer . 当你使用IntInteger它选择Any ,但是它很混乱,因为列表中Int的运行时表示是Integer So it looks like it used the implicit conversion, but it didn't. 所以看起来它使用了隐式转换,但事实并非如此。 You can verify by opening a Scala prompt... 您可以通过打开Scala提示进行验证...

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

I don't know anything aboutjOOQ, but I think the issue is that Scala does not understand java generics very well. 我对jOOQ一无所知,但我认为问题在于Scala不能很好地理解java泛型。 Try: 尝试:

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)

Sounds familiar?? 听起来很熟悉??

And to fix, just inform the type T to call the method: test[Integer](a,b) works fine. 要修复,只需通知类型T调用方法: test[Integer](a,b)工作正常。

EDIT: 编辑:

There a few things involved here: 这里涉及到一些事情:

  1. Erasure -> When compiled the type of the generic will disappear by erasure. 擦除 - >编译时,通用的类型将通过擦除消失。 The compiler will use Object which Scala, will treat as Any. 编译器将使用Scala将被视为Any的Object。 However a ArrayList[Integer] is not an ArrayList[Any], even though Integer is any. 但是,ArrayList [Integer]不是ArrayList [Any],即使Integer是any。 The same way that TableField[gen.tables.records.DocRecord,Integer] is not a Field[Any]. 与TableField [gen.tables.records.DocRecord,Integer]相同的方式不是Field [Any]。

  2. Type inference mechanism -> it will figure out what type T should be and to do that it will use the intersection dominator of the types passed (in our case the first common ancestor). 类型推断机制 - >它将确定T应该是什么类型,并且这样做它将使用传递的类型的交集支配者(在我们的例子中是第一个共同的祖先)。 Page 36 of Scala Language Spec , which in our examples above will lead use to Any. Scala语言规范的第36页,在我们上面的例子中将使用Any。

  3. Implicit conversion -> it is the last step and would be called if there was some type to be converted to another one, but since the type of the arguments were determined to be the first common ancestor, there is no need to convert and we will never have a implicit conversion if we don't force the type T. 隐式转换 - >它是最后一步,如果有某种类型要转换为另一种类型,则会被调用,但由于参数的类型被确定为第一个共同的祖先,因此无需转换,我们将如果我们不强制类型T,永远不会有隐式转换。

A example to show how the common ancestor is used to determine 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

Summing up, the implicit method does not get called, because the type inference mechanism, determines the types before getting the argument and since it uses the common ancestor, there is no need for a implicit conversion. 总而言之,隐式方法不会被调用,因为类型推断机制在获取参数之前确定类型,并且因为它使用共同的祖先,所以不需要隐式转换。

Your code produces an error due to erasure mechanism which disappear with the type information that would be important to determine the correct type of the argument. 您的代码由于擦除机制而产生错误,该机制随类型信息消失,这对于确定参数的正确类型很重要。

@RobN, thanks for questioning my answer, I learned a lot with the process. @RobN,感谢您质疑我的回答,我从这个过程中学到了很多东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM