在 Scala 中使用结构类型出现 NoSuchMethodException
Using Structural Type in Scala occurs NoSuchMethodException
我正在编写一个具有三个参数的函数 f
、from
、to
。 f
应该是具有 apply
方法的任何对象,该方法消耗并产生 Int。
def printValues(f: {def apply(n: Int):Int}, from: Int, to: Int) {
for(i <- from to `to`) print(f(i) + " ")
print("\n")
}
我在这里使用结构类型来保证f
有apply()
方法。
当我用 Array[Int]
调用方法 printValues()
时,一切顺利。
printValues(Array(1,1,2,3,5,8,13,21,34,55), 3, 6)
然后我尝试用 lambda 表达式调用该方法,结果一团糟
printValues((x: Int) => x * x, 3, 6)
错误信息
java.lang.NoSuchMethodException: ch18.p8.Main$$$Lambda/474675244.apply(int)
at java.lang.Class.getMethod(Class.java:1786)
at ch18.p8.Main$.reflMethod$Method3(Ch18.scala:270)
at ch18.p8.Main$.$anonfun$printValues(Ch18.scala:270)
at scala.runtime.java8.JFunction1$mcII$sp.apply(JFunction1$mcII$sp.java:12)
at scala.collection.TraversableLike.$anonfun$map(TraversableLike.scala:234)
at scala.collection.immutable.Range.foreach(Range.scala:156)
at scala.collection.TraversableLike.map(TraversableLike.scala:234)
at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
at scala.collection.AbstractTraversable.map(Traversable.scala:104)
at ch18.p8.Main$.printValues(Ch18.scala:270)
at ch18.p8.Main$.delayedEndpoint$ch18$p8$Main(Ch18.scala:274)
at ch18.p8.Main$delayedInit$body.apply(Ch18.scala:267)
at scala.Function0.apply$mcV$sp(Function0.scala:34)
at scala.Function0.apply$mcV$sp$(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App.$anonfun$main$adapted(App.scala:76)
at scala.collection.immutable.List.foreach(List.scala:389)
at scala.App.main(App.scala:76)
at scala.App.main$(App.scala:74)
at ch18.p8.Main$.main(Ch18.scala:267)
at ch18.p8.Main.main(Ch18.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run(ScalaClassLoader.scala:98)
at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:32)
at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:30)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:129)
at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:98)
at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:90)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:129)
at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:22)
at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:21)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
at scala.tools.nsc.MainGenericRunner.runTarget(MainGenericRunner.scala:61)
at scala.tools.nsc.MainGenericRunner.run(MainGenericRunner.scala:88)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:99)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:104)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
我尝试验证对象 lambdaxxx
是否具有函数 apply
(在 Scala REPL 中)
scala> val f = (x: Int) => x * x
f: Int => Int = $$Lambda20/826690115@7f8633ae
scala> f.apply
def apply(v1: Int): Int
我收到了两个错误报告:
Seemingly using structural type
所以说不定我也掉进了这个圈套
顺便说一句,如果printValues()
在下面,lambda表达式可以适合方法printValues2()
。
def printValues2(f: (Int) => (Int), from: Int, to: Int) {
for(i <- from to `to`) print(f(i) + " ")
print("\n")
}
感谢您分享您的想法,祝您好运。
似乎是 Scala 编译器 Scala 2.12 编译器 中的错误 不生成 指定类型方法:
val t1 = (x: String) => x + x
t1.getClass.getMethods.foreach(println)
...
public java.lang.Object TestFooBar$$$Lambda/1265210847.apply(java.lang.Object)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
上面代码中,从高阶函数f1
获取所有方法,发现只有Object ... apply(java.lang.Object)
方法,没有String ... apply(java.lang.String)
方法,所以当传递给 structural type
: f: {def apply(n: String):String}
时,它会抛出 NoSuchMethodException
.
val t2 = new Function1[String, String] {
def apply(x: String): String = x + x
}
...
public java.lang.String TestFooBar$$anon.apply(java.lang.String)
public java.lang.Object TestFooBar$$anon.apply(java.lang.Object)
...
在上面的代码中,我们为此使用了anonymous class Function1
。本质上高阶函数:(x: String) => x + x
等于new Function1[String, String]
。但是我们可以发现 anonymous class 生成了一个 specify type 方法:String ... apply(java.lang.String)
.
所以这可能是 Scala 2.12 编译器问题,没有为 高阶函数生成 指定类型 方法.
以及为什么要使用 String 而不是 Int 进行此测试?
由于Function1
有@specialized原始类型(Int),需要避免这种影响。
只是对此错误和解决方法的部分解释。
结构类型正在使用反射,所以每次你从结构类型调用一个方法时(比如你的例子中的 f(i)
)你正在做类似 f.getClass.getMethod.invoke(i)
的事情(所以方法解析发生在运行时).让我们简化您的代码 (2.12.2 REPL):
scala> val f: {def apply(n: Int):Int} = (x: Int) => x * x
f: AnyRef{def apply(n: Int): Int} = $$Lambda50/571435580@5eb041b5
scala> f(4)
java.lang.NoSuchMethodException: $$Lambda50/571435580.apply(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:13)
... 29 elided
并反省它以查看 f
在运行时实际上有哪些方法
scala> f.getClass.getMethods
res21: Array[java.lang.reflect.Method] = Array(public int $$Lambda50/571435580.apply$mcII$sp(int), public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long)
scala> f.getClass.getMethods()(0)
res23: java.lang.reflect.Method = public int $$Lambda50/571435580.apply$mcII$sp(int)
在这里你可以很容易地看到 scala(粗略地说)将你的 Int => Int
lambda 编译成 apply$mcII$sp
方法,但是反射调用正在寻找未添加到的 apply(int)
方法出于某种原因的最终字节码。
解决方法如下:
scala> val m = f.getClass.getMethods()(0)
m: java.lang.reflect.Method = public int $$Lambda50/571435580.apply$mcII$sp(int)
scala> m.setAccessible(true)
scala> m.invoke(f, new Integer(5))
res50: Object = 25
如果 f(i)
抛出异常,您可能必须这样做作为备份。
但是要求 Int => Int
当然要快得多:)
我正在编写一个具有三个参数的函数 f
、from
、to
。 f
应该是具有 apply
方法的任何对象,该方法消耗并产生 Int。
def printValues(f: {def apply(n: Int):Int}, from: Int, to: Int) {
for(i <- from to `to`) print(f(i) + " ")
print("\n")
}
我在这里使用结构类型来保证f
有apply()
方法。
当我用 Array[Int]
调用方法 printValues()
时,一切顺利。
printValues(Array(1,1,2,3,5,8,13,21,34,55), 3, 6)
然后我尝试用 lambda 表达式调用该方法,结果一团糟
printValues((x: Int) => x * x, 3, 6)
错误信息
java.lang.NoSuchMethodException: ch18.p8.Main$$$Lambda/474675244.apply(int)
at java.lang.Class.getMethod(Class.java:1786)
at ch18.p8.Main$.reflMethod$Method3(Ch18.scala:270)
at ch18.p8.Main$.$anonfun$printValues(Ch18.scala:270)
at scala.runtime.java8.JFunction1$mcII$sp.apply(JFunction1$mcII$sp.java:12)
at scala.collection.TraversableLike.$anonfun$map(TraversableLike.scala:234)
at scala.collection.immutable.Range.foreach(Range.scala:156)
at scala.collection.TraversableLike.map(TraversableLike.scala:234)
at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
at scala.collection.AbstractTraversable.map(Traversable.scala:104)
at ch18.p8.Main$.printValues(Ch18.scala:270)
at ch18.p8.Main$.delayedEndpoint$ch18$p8$Main(Ch18.scala:274)
at ch18.p8.Main$delayedInit$body.apply(Ch18.scala:267)
at scala.Function0.apply$mcV$sp(Function0.scala:34)
at scala.Function0.apply$mcV$sp$(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App.$anonfun$main$adapted(App.scala:76)
at scala.collection.immutable.List.foreach(List.scala:389)
at scala.App.main(App.scala:76)
at scala.App.main$(App.scala:74)
at ch18.p8.Main$.main(Ch18.scala:267)
at ch18.p8.Main.main(Ch18.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run(ScalaClassLoader.scala:98)
at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:32)
at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:30)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:129)
at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:98)
at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:90)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:129)
at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:22)
at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:21)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
at scala.tools.nsc.MainGenericRunner.runTarget(MainGenericRunner.scala:61)
at scala.tools.nsc.MainGenericRunner.run(MainGenericRunner.scala:88)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:99)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:104)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
我尝试验证对象 lambdaxxx
是否具有函数 apply
(在 Scala REPL 中)
scala> val f = (x: Int) => x * x
f: Int => Int = $$Lambda20/826690115@7f8633ae
scala> f.apply
def apply(v1: Int): Int
我收到了两个错误报告:
Seemingly using structural type
所以说不定我也掉进了这个圈套
顺便说一句,如果printValues()
在下面,lambda表达式可以适合方法printValues2()
。
def printValues2(f: (Int) => (Int), from: Int, to: Int) {
for(i <- from to `to`) print(f(i) + " ")
print("\n")
}
感谢您分享您的想法,祝您好运。
似乎是 Scala 编译器 Scala 2.12 编译器 中的错误 不生成 指定类型方法:
val t1 = (x: String) => x + x
t1.getClass.getMethods.foreach(println)
...
public java.lang.Object TestFooBar$$$Lambda/1265210847.apply(java.lang.Object) public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
上面代码中,从高阶函数f1
获取所有方法,发现只有Object ... apply(java.lang.Object)
方法,没有String ... apply(java.lang.String)
方法,所以当传递给 structural type
: f: {def apply(n: String):String}
时,它会抛出 NoSuchMethodException
.
val t2 = new Function1[String, String] {
def apply(x: String): String = x + x
}
...
public java.lang.String TestFooBar$$anon.apply(java.lang.String) public java.lang.Object TestFooBar$$anon.apply(java.lang.Object)
...
在上面的代码中,我们为此使用了anonymous class Function1
。本质上高阶函数:(x: String) => x + x
等于new Function1[String, String]
。但是我们可以发现 anonymous class 生成了一个 specify type 方法:String ... apply(java.lang.String)
.
所以这可能是 Scala 2.12 编译器问题,没有为 高阶函数生成 指定类型 方法.
以及为什么要使用 String 而不是 Int 进行此测试?
由于Function1
有@specialized原始类型(Int),需要避免这种影响。
只是对此错误和解决方法的部分解释。
结构类型正在使用反射,所以每次你从结构类型调用一个方法时(比如你的例子中的 f(i)
)你正在做类似 f.getClass.getMethod.invoke(i)
的事情(所以方法解析发生在运行时).让我们简化您的代码 (2.12.2 REPL):
scala> val f: {def apply(n: Int):Int} = (x: Int) => x * x
f: AnyRef{def apply(n: Int): Int} = $$Lambda50/571435580@5eb041b5
scala> f(4)
java.lang.NoSuchMethodException: $$Lambda50/571435580.apply(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:13)
... 29 elided
并反省它以查看 f
在运行时实际上有哪些方法
scala> f.getClass.getMethods
res21: Array[java.lang.reflect.Method] = Array(public int $$Lambda50/571435580.apply$mcII$sp(int), public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long)
scala> f.getClass.getMethods()(0)
res23: java.lang.reflect.Method = public int $$Lambda50/571435580.apply$mcII$sp(int)
在这里你可以很容易地看到 scala(粗略地说)将你的 Int => Int
lambda 编译成 apply$mcII$sp
方法,但是反射调用正在寻找未添加到的 apply(int)
方法出于某种原因的最终字节码。
解决方法如下:
scala> val m = f.getClass.getMethods()(0)
m: java.lang.reflect.Method = public int $$Lambda50/571435580.apply$mcII$sp(int)
scala> m.setAccessible(true)
scala> m.invoke(f, new Integer(5))
res50: Object = 25
如果 f(i)
抛出异常,您可能必须这样做作为备份。
但是要求 Int => Int
当然要快得多:)