1

私はこのコードを期待しています(洗練された型でパターンマッチを使用した後に匿名クラスのメソッドを呼び出します)

(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$NameOfSomeAnonymousClassThatHasAfooMethod

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プライベートとして生成されていることがわかりました。回答でこの原因について推測しましたが、よくわかりません。アイデアがあれば、回答として投稿してください。

4

3 に答える 3

2

型の消去のために一致が常に成功することは知っていますが、 f の実行時の型 (消去を考慮しても) は $anon$NameOfSomeAnonymousClassThatHasAfooMethod である必要があるため、問題が発生することはありません。

それは明らかな実装であり、期待どおりであるという意味で「すべき」です。あなたが見つけたように、そうである必要はありません。

リファインメントでのパターン マッチングはブラインド キャストです。あなたは多くの信仰を持たなければなりません。

Scala メソッドはデフォルトで public であるはずなので、これは奇妙です。

ソースで宣言するメソッドは、デフォルトで public です。実装の詳細はありません。

原因は、クラスが匿名であるため、定義されたメソッドをクラスの外部から呼び出すことができないとコンパイラが誤って想定しているためだと思われます。

コンパイラは、匿名クラスのメソッドを直接呼び出すには取り決めの条件に違反する必要があると正しく想定しています。あなたは参照をキャストし、チャンスをつかみます。

于 2012-07-23T08:51:26.860 に答える
1

さらに調査したところ、このメソッドはどういうわけか非公開になっていることがわかりました。

(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が指摘しているように、fooを削除すると機能します(メソッドはパブリックです): Any

投機的に

原因は、クラスが匿名であり、インスタンスが別の型であると宣言されているため、その定義されたメソッドをクラスの外部から呼び出すことができないとコンパイラが誤って想定しているためだと思います。この仮定はJavaでは有効ですが、構造型を提供する言語では有効ではありません。したがって、情報隠蔽の原則を熱心に適用して、それらをプライベートとして生成します。もしそうなら、これはコンパイラのバグまたは言語設計のコーナーケースのいずれかです(無名関数を構造型付けと一緒に使用します)。

于 2012-07-23T08:31:02.063 に答える
1

コメントで推測したように、問題は、匿名クラスをAnyにアップキャストすると、コンパイラが匿名で定義されたメソッドの可視性を自動的に制限することです。

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

定義上、どのスーパークラスにも属していない匿名クラスで作成したメソッドは、作成したばかりのオブジェクトでのみ使用できます。ただし、オブジェクトをすぐにAnyにアップキャストすると、メソッドfooを安全に呼び出すことができる匿名クラスのタイプセーフなインスタンスはありません。

于 2012-07-23T09:46:00.797 に答える