当前位置: 首页 > 知识库问答 >
问题:

当这样的方法存在时,为什么会抛出'NoSuchMeodExcture'?

申屠宏胜
2023-03-14

我期望这段代码(在对细化类型使用模式匹配后调用匿名类的方法)

(new {
    def foo : Unit = println("Called foo method")
} : Any) match {
    case f : {def foo : Unit} ⇒
        println("Has foo method")
        f.foo
}

要打印

Has foo method
Called foo method

(以及未经检查的警告)。

我知道由于类型擦除,匹配总是成功的,但这不应该导致问题,因为f的运行时类型(甚至考虑擦除)应该是$anon$某个匿名类的名称

当输入Scala REPL(2.9.1)时,它实际上会抛出NoSuchMethodException

<console>:11: warning: refinement AnyRef{def foo: Unit} in type pattern AnyRef{def foo: Unit} is unchecked since it is eliminated by erasure
              case f : {def foo : Unit} ⇒
                       ^
Has foo method
java.lang.NoSuchMethodException: $anon$1.foo()
        at java.lang.Class.getMethod(Class.java:1622)
        at .reflMethod$Method1(<console>:13)
        at .<init>(<console>:13)
        at .<clinit>(<console>:13)
        at .<init>(<console>:11)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
        at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
        at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
        at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
        at java.lang.Thread.run(Thread.java:679)

为什么?

事实证明,最直接的原因是foo被生成为private。我在回答中推测了原因,但我不确定。如果你有一个想法,仍然可以将其作为答案发布!


共有3个答案

闻人宜
2023-03-14

正如我在一篇评论中猜测的那样,问题在于,如果将匿名类向上转换为Any,编译器会自动限制匿名定义方法的可见性。

(new {
    def foo : Unit = println("Called foo method")
} ) match {
    case f : {def foo : Unit} ⇒
        println("Has foo method")
        f.getClass.getDeclaredMethods
}

根据定义,您在不属于其任何超类的匿名类中创建的方法将仅在您刚刚创建的对象上可用。但是,如果您立即将对象向上转换为Any,那么您将无法安全地调用方法foo的匿名类的类型安全实例。

华子昂
2023-03-14

经过进一步调查,我发现该方法不知何故被保密:

(new {
    def foo : Unit = println("Called foo method")
} : Any) match {
    case f : {def foo : Unit} ⇒
        println("Has foo method")
        f.getClass.getDeclaredMethods
}

打印res5:Array[java.lang.reflect.Method]=Array(private void$anon$1.foo())

这很奇怪,因为Scala方法在默认情况下应该是公共的。

正如Edmondo1984所指出的,如果您删除了: any,它就可以工作(foo方法是公共的)。

我怀疑原因是编译器错误地认为,由于类是匿名的,并且实例被声明为另一种类型,因此其定义的方法无法从类外部调用。这种假设在Java中是有效的,但在提供结构类型的语言中是无效的。因此,它将它们生成为私有的,过度使用了信息隐藏的原理。如果是这样的话,这要么是一个编译器错误,要么是一个语言设计角案例(使用匿名函数和结构类型)。

靳金鹏
2023-03-14

我知道由于类型擦除,匹配总是成功的,但这不应该引起问题,因为f的运行时类型(即使考虑擦除)应该是$anon$NameOf某位匿名ClassThatHasAfoomethod

它“应该”,因为这是显而易见的实现,也是你所期望的;它不需要,正如你所发现的,它不需要。

细化上的模式匹配是盲铸造。你必须有很大的信心。

这很奇怪,因为Scala方法在默认情况下应该是公共的。

默认情况下,在源代码中声明的方法是公共的。具体实施细节尚未公布。

我怀疑原因是编译器错误地假设,由于类是匿名的,它定义的方法在类外部是不可调用的。

编译器正确地假设您必须违反安排条款才能直接调用匿名类的任何方法。你投你的推荐信,你抓住你的机会。

 类似资料:
  • 问题内容: 考虑以下Java源代码: 该是。 为什么该语句有时会抛出? 谢谢。 问题答案: 线程安全 如果您的代码是多线程的,则有可能。例如: 如果在语句执行之后(但在循环之前)立即将另一个线程设置为,则您将获得一个。通过使用访问器(与延迟初始化结合使用)可以避免这种情况。 另外,如其他人所提到的,如果可能,请避免使用有利于泛型的此类循环构造。有关详细信息,请参见其他答案。 配件提供保护 如果始终

  • 问题内容: 我正在从我的一个类中执行Java二进制文件,并且抛出ClassNotFoundException: Geoline类位于/home/geo/Geoline.java。关键是,无论我位于文件系统中的哪个位置,如果我手动执行相同的命令,都将执行该类。使用Runtime.getRuntime()。exec执行二进制文件时,为什么不会发生相同的事情? 编辑:这是在verbose标记上生成的输出

  • 问题内容: 假设我将Sun的JVM嵌入C ++应用程序中。通过JNI,我调用了Java方法(我自己的),该方法又调用了我在共享库中实现的本机方法。 如果此本机方法抛出C ++异常,会发生什么? 编辑:编译器是gcc 3.4.x,jvm是sun的1.6.20。 问题答案: 在JNI文献中,“ 异常 ”一词似乎专门用于指代Java异常。本机代码中发生的意外事件称为 编程错误 。JNI明确不需要JVM检

  • 如果你注释掉的内部循环,它会抛出,这显然是因为我们对集合做了更改。 如果取消注释,为什么这个循环允许我们添加这两项?运行它需要一段时间(在奔腾CPU上)大约半分钟,但它不会抛出,有趣的是,它输出: 这有点出乎意料,但它表明我们可以改变,它实际上改变了集合。知道为什么会发生这种行为吗?

  • 我试图在Java中将扫描字符串转换为JSONObject,并从JSON Simple导入JSON模块。我已经在构建路径中添加了JSON-simple-1.1.1.jar。然而,当我试图传递内联字符串时。我的内联字符串如下:{“events”:[{“url”:“/操纵多云国家”,“visitorId”:“03c0b5e0-3eeb-382a-8c6d-35e4bb189cf6”,“timestamp

  • 我有一个Spring/JPA配置,其中Hibernate作为持久性提供者。但是,我不明白为什么在没有打开事务的情况下对以下DAO代码调用save()时没有抛出TransactionRequiredException(DAO/服务中没有@Transactional): 正如预期的那样,实体没有保存,但为什么没有引发异常?持久化的javadoc表示,持久化()应该抛出一个“Transaction必需的