Rob*_*b N 5 scala implicit type-mismatch jooq
我试图调用此set
方法记录在这里,Java库jOOQ,有签名:
<T> ... set(Field<T> field, T value)
Run Code Online (Sandbox Code Playgroud)
这个Scala系列是一个问题:
.set(table.MODIFIED_BY, userId)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
更新 - 关于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")
}
Run Code Online (Sandbox Code Playgroud)
你会看到它调用test,但绝不会从String
"abc" 隐式转换为Foo
.相反,它正在选择一个,T
因为test[T]
它是String
和之间的共同基类Foo
.当您使用Int
与Integer
它选择Any
,但它的混淆,因为的运行时表示Int
不在列表中Integer
.所以看起来它使用了隐式转换,但事实并非如此.您可以通过打开Scala提示进行验证...
scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2)
List[Any]
Run Code Online (Sandbox Code Playgroud)
我对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)
Run Code Online (Sandbox Code Playgroud)
听起来很熟悉??
要修复此问题,只需通知类型 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
Run Code Online (Sandbox Code Playgroud)
综上所述,隐式方法不会被调用,因为类型推断机制在获取参数之前确定类型,并且由于它使用共同祖先,因此不需要隐式转换。
由于擦除机制,您的代码会产生错误,该错误会随着类型信息而消失,而类型信息对于确定参数的正确类型非常重要。
@RobN,感谢您质疑我的答案,我在这个过程中学到了很多东西。