![](/img/trans.png)
[英]Why does Scala implicit conversion work here with two args but not with one?
[英]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>
。 userId
是Int
。 Predef
有一个从Int
到Integer
的隐式转换,那么它为什么不使用呢? 我明白了:
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
,它是String
和Foo
之间的公共基类。 当你使用Int
和Integer
它选择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)
工作正常。
编辑:
这里涉及到一些事情:
擦除 - >编译时,通用的类型将通过擦除消失。 编译器将使用Scala将被视为Any的Object。 但是,ArrayList [Integer]不是ArrayList [Any],即使Integer是any。 与TableField [gen.tables.records.DocRecord,Integer]相同的方式不是Field [Any]。
类型推断机制 - >它将确定T应该是什么类型,并且这样做它将使用传递的类型的交集支配者(在我们的例子中是第一个共同的祖先)。 Scala语言规范的第36页,在我们上面的例子中将使用Any。
隐式转换 - >它是最后一步,如果有某种类型要转换为另一种类型,则会被调用,但由于参数的类型被确定为第一个共同的祖先,因此无需转换,我们将如果我们不强制类型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.