Java 当这样一个方法存在时,为什么会抛出'NoSuchMethodException'?

Java 当这样一个方法存在时,为什么会抛出'NoSuchMethodException'?,java,scala,reflection,jvm,anonymous-class,Java,Scala,Reflection,Jvm,Anonymous Class,我希望这段代码在对优化类型使用模式匹配后调用匿名类的方法 (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 以及未经检查的警告 我知道由于类型擦除,匹配总是成功的

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

(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:

为什么?

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

经过进一步调查,我发现该方法不知何故被私有化了:

(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]=Arrayprivate void$anon$1.foo

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

正如Edmondo1984所指出的,如果删除:Any,foo方法是公共的,那么它是有效的

推测地
我怀疑原因是编译器错误地认为,由于类是匿名的,并且实例被声明为另一种类型,因此其定义的方法无法从类外部调用。这种假设在Java中是有效的,但在提供结构类型的语言中是无效的。因此,它产生了他们作为私人,在一个过分热心的应用原则。如果是这样,这可能是一个编译器错误,也可能是一个使用匿名函数和结构类型的语言设计角案例。

经过进一步调查,我发现该方法以某种方式被私有化了:

(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]=Arrayprivate void$anon$1.foo

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

正如Edmondo1984所指出的,如果删除:Any,foo方法是公共的,那么它是有效的

推测地 我怀疑原因是编译器错误地认为,由于类是匿名的,并且实例被声明为另一种类型,因此其定义的方法无法从类外部调用。这种假设在Java中是有效的,但在提供结构类型的语言中是无效的。因此,它产生了他们作为私人,在一个过分热心的应用原则。如果是这样,这要么是编译器错误,要么是使用匿名函数和结构类型的语言设计角案例

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

从某种意义上讲,它应该是显而易见的实现,也是您所期望的;它不需要,正如你所发现的,它不需要

细化上的模式匹配是一种盲转换。你必须有很大的信心

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

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

我怀疑原因是编译器错误地认为 该类是匿名的,其定义的方法不可从中调用 课外活动

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

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

从某种意义上讲,它应该是显而易见的实现,也是您所期望的;它不需要,正如你所发现的,它不需要

细化上的模式匹配是一种盲转换。你必须有很大的信心

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

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

我怀疑原因是编译器错误地认为 该类是匿名的,其定义的方法不可从中调用 课外活动


编译器正确地假设您必须违反协议条款才能直接调用匿名类的任何方法。你抛出了引用,你就抓住了机会。

正如我在评论中猜测的那样,问题是如果你将匿名类向上抛出,编译器会自动限制匿名定义方法的可见性

(new {
    def foo : Unit = println("Called foo method")
} ) match {
    case f : {def foo : Unit} ⇒
        println("Has foo method")
        f.getClass.getDeclaredMethods
}
根据定义,您在不属于其任何超类的匿名类中创建的方法将仅在您刚刚创建的对象上可用。但是,如果您立即将对象向上转换为Any,则不会有匿名类的类型安全实例,您将能够在该实例上安全地调用方法foo。

在注释中使用,问题是如果将匿名类向上转换为Any,编译器会自动限制匿名定义方法的可见性

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

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

不是原因,因为这里foo是公共的。您确定这是任何损坏后的名称吗?我会打印所有要检查的getMethods。我知道这不太受欢迎,但我会避开结构类型:-s@PeterLawrey:如果我使用f.getClass.getMethods foreach println,它只列出对象方法。但是为什么它给错了班级呢?不是原因,因为这里的foo是公共的。你确定这是任何损坏后的名称吗?我会打印所有要检查的getMethods。我知道这不太受欢迎,但我会避开结构类型:-s@PeterLawrey:如果我使用f.getClass.getMethods foreach println,它只列出对象方法。但是为什么它给出了错误的类呢?但是foo是一个在源代码中声明的方法,而不是一个实现细节。如果它是一个错误的强制转换,它将是一个ClassCastException,而不是NoSuchMethodException。如果编译器确定代码违反了协议条款,这不应该是编译时错误吗?foo不是任何可访问类的成员。我并不是严格意义上的jvm级cast使用cast,而是说您正在推翻编译器,并得到您所得到的。这也是3的答案。一旦你投下,所有的赌注都结束了。编译器不会阻止你。你可以相信我,因为我写了相当多的相关代码。我认为这是一个bug,对于静态类型语言,NoSuchMethodException是不可接受的。如果无法修复,为什么允许结构类型匹配?但foo是在源代码中声明的方法,而不是实现细节。如果它是错误的强制转换,它将是ClassCastException,而不是NoSuchMethodException。如果编译器确定代码违反了协议条款,这不应该是编译时错误吗?foo不是任何可访问类的成员。我并不是严格意义上的jvm级cast使用cast,而是说您正在推翻编译器,并得到您所得到的。这也是3的答案。一旦你投下,所有的赌注都结束了。编译器不会阻止你。你可以相信我,因为我写了相当多的相关代码。我认为这是一个bug,对于静态类型语言,NoSuchMethodException是不可接受的。如果不能修复,为什么会允许结构类型匹配?事实上,现在我怀疑这是原因。如果是这样的话,新的{def foo=123}.foo应该失败,但实际上它按预期的方式工作,返回123。@Edmondo1984:现在foo被生成为public。这就解释了。事实上,现在我怀疑这是原因。如果是这样的话,新的{def foo=123}.foo应该失败,但实际上它按预期的方式工作,返回123。@Edmondo1984:现在foo被生成为public。这就解释了。