我有以下代码段(我认为)在通用类型addNumber1(x:T):T
上定义了一个方法T
,它是AnyVal
的子类型,并且有一个方法+(s:Int):T
。
def addNumber1[T <: AnyVal {def +(s:Int):T}](x:T):T = {x + 1}
addNumber1(31) // compiles but throws exception
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:8)
at .addNumber1(<console>:8)
... 33 elided
我尝试添加import scala.language.reflectiveCalls
来抑制功能警告,但仍然会收到错误消息。
我可以在使用AnyRef
或Any
时使用此功能,如下所示:
def addNumber1[T <: Any {def +(s:Int):T}](x:T):T = {x + 1}
class Foo(s:String) {def +(i:Int) = new Foo((s+1).toString)} // random code
class Bar(s:Foo) {def +(i:Int) = new Bar(new Foo(i.toString))} // random code
addNumber1(new Foo("1")) // works
addNumber1(new Bar(new Foo("1"))) // works
addNumber1(1) // compiles but gives exception
答案 0 :(得分:1)
你遇到了很多功能的交叉点:
就Scala编译器的初始阶段而言(包括类型检查),Int
确实有(重载)+
方法。但是这个“方法”会被后期阶段特别处理(就像Int
上的所有方法一样,因为它不是真正的类)。
在Scala中定义的名为+
的方法被转换为字节码中名为$plus
的方法,因为+
不是那里的合法标识符。由于+
上的Int
是特殊的,如上所述,这不适用于它。由于结构类型是使用Java反射实现的,因此addNumber1
看起来有点像
def addNumber1(x: Object) = x.getClass.getMethod("$plus").invoke(x, 1)
要在addNumber1
上致电int
,必须首先将其设置为Integer
,因为int
不是对象。 Integer
,不是Scala类型,没有$plus
方法。在Scala中,可以编写类似val x: Integer = ...; x + 1
的内容,但这会使用Java反射不知道的隐式转换。
答案 1 :(得分:0)
我认为问题与AnyVal,AnyRef或Any无关。
addNumber1(new Foo("1"))
这是有效的,因为你确实定义了一个提供def +(s:Int)的实现的Foo类:T。
addNumber1(1)
这不起作用,因为Integer类没有提供它,如异常中所述:
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)