问题
I have the following snippet that (I think) defines a method addNumber1(x:T):T
on a generic type T
which is a subtype of AnyVal
and has a method +(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
I tried adding import scala.language.reflectiveCalls
to suppress a feature warning but still get the error.
I am able to use this when working with AnyRef
or Any
as below:
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
回答1:
You run into an intersection of quite a few features:
So far as the initial stages of Scala compiler are concerned (including typechecking),
Int
does have an (overloaded)+
method. But this "method" is treated specially by the later stages (as are all methods onInt
, because it isn't really a class).Methods called
+
and defined in Scala are translated to methods called$plus
in bytecode, since+
is not a legal identifier there. Since+
onInt
is special, as mentioned above, this doesn't apply to it. Since the structural types are implemented using Java reflection, youraddNumber1
looks somewhat likedef addNumber1(x: Object) = x.getClass.getMethod("$plus").invoke(x, 1)
To call
addNumber1
on anint
, it has to be boxed toInteger
first, becauseint
is not an object.Integer
, not being a Scala type, doesn't have a$plus
method. In Scala you can write something likeval x: Integer = ...; x + 1
, but this uses an implicit conversion which Java reflection has no idea about.
回答2:
I think the problem has nothing to do with AnyVal, AnyRef Or Any.
addNumber1(new Foo("1"))
This works because you indeed defined a Foo class that provides an implementation of def +(s:Int):T.
addNumber1(1)
This doesn't work because Integer class doesn't provide it, as is mentioned in the exception:
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)
来源:https://stackoverflow.com/questions/39315310/getting-java-lang-nosuchmethodexception-in-structural-typing-of-anyval