在AnyVal的结构类型中获取java.lang.NoSuchMethodException

时间:2016-09-04 09:09:47

标签: scala structural-typing

我有以下代码段(我认为)在通用类型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来抑制功能警告,但仍然会收到错误消息。

我可以在使用AnyRefAny时使用此功能,如下所示:

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

2 个答案:

答案 0 :(得分:1)

你遇到了很多功能的交叉点:

  1. 就Scala编译器的初始阶段而言(包括类型检查),Int确实有(重载)+方法。但是这个“方法”会被后期阶段特别处理​​(就像Int上的所有方法一样,因为它不是真正的类)。

  2. 在Scala中定义的名为+的方法被转换为字节码中名为$plus的方法,因为+不是那里的合法标识符。由于+上的Int是特殊的,如上所述,这不适用于它。由于结构类型是使用Java反射实现的,因此addNumber1看起来有点像

    def addNumber1(x: Object) = x.getClass.getMethod("$plus").invoke(x, 1)
    
  3. 要在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)