2017-02-17 3 views
4

Java/JVM仕様の不完全さという、次のような奇妙なケースに直面しました。今)Javaでインタフェースする静的メソッドを持つクラスを変更するバイナリ互換性

User.mainを(実行
public interface Class { 
    public static void foo() { 
    System.out.println("hi"); 
    } 
} 

:ユーザーを再コンパイルせずにインターフェースするクラスを再コンパイル後

public class Class { 
    public static void foo() { 
    System.out.println("hi"); 
    } 
} 

public class User { 
    public static void main(String[] args) { 
    Class.foo(); 
    } 
} 

:私たちはクラスを(我々は、Java 1.8とのHotSpotを使用します)があるとし同じ出力 'hi'を生成します。これは明白なようだが、私はそれがでIncompatibleClassChangeErrorで失敗する期待と理由です:私はインターフェイスにクラスを変更するとJVM 5.3.5#3 statementによるとバイナリ互換性であることを知っている

クラスまたはインタフェース場合実際にはCの直接スーパークラスという名前のインターフェイスは、IncompatibleClassChangeErrorをスローします。

しかし、我々はクラスの後継を持っていないと仮定してみましょう。メソッドの解決方法については、JVM仕様を参照する必要があります。 最初のバージョンは、このバイトコードにコンパイルされています

public static void main(java.lang.String[]); 
    Code: 
     0: invokestatic #2     // Method examples/Class.foo:()V 
     3: return 

だから我々はここclasspoolで「CONSTANT_Methodref_info」と呼ばれるものを持っています。

invokestaticの動作を引用しましょう。

...そのインデックスにおける実行時定数プール項目は、名前と記述(§4.3を与える方法又はインタフェース方法(§5.1)へのシンボリック参照しなければなりません。 3)と、メソッドが見つかるクラスまたはインタフェースへのシンボリックな参照。指定されたメソッドは解決済み(§5.4.3.3)です。

したがって、JVMはメソッドとインターフェイスのメソッドを異なる方法で処理します。私たちの場合、JVMはメソッドがクラスのメソッド(インタフェースではない)であるとみなします。

JVMの仕様によれば、JVMは、次の文に失敗しなければならない:

1)Cインターフェイスである場合、この方法の解像度がでIncompatibleClassChangeErrorがスロー JVMは、それに応じて分解能5.4.3.3方法は、それを解決しようと。

Classは実際にクラスではなくインターフェイスなので、

残念ながら、Java言語仕様第13章バイナリ互換性でクラスをインタフェースに変更することのバイナリ互換性に関する記述は見つかりませんでした。さらに、同じ静的メソッドを参照するというようなトリッキーなケースについては何も言及されていません。

誰かがそれについて詳しく説明して、私が何かを見逃したら私に見せてもらえますか?

+1

ところで、私は[言及](https://wiki.eclipse.org/Evolving_Java-based_APIs_2#Evolving_API_packages)を見つけました。 'APIクラスインターフェイスの性別の変更は、クラス/インタフェースは、クライアントによって使用されますが、クライアントでは使用されません。これは、インタフェースで宣言されたメソッドを呼び出すためのJava VMバイトコードが、クラスで宣言されたメソッドを呼び出すために使用されたバイトコードと異なるためです。しかし、それは非公式なものです。 –

答えて

3

まず、あなたの例に継承が含まれていないため、「クラスの継承者を持たないと仮定する」必要はありません。したがって、5.3.5の引用部分はこの例とは無関係です。

メソッドまたはインターフェイスメソッドへのシンボリックリファレンス」という名前の§6.5の引用部分は、皮肉なことにchange made an Java 8 to relax the restrictionsです。 命令は、staticの場合、インターフェイスメソッドで明示的に呼び出すことができます。

最終的に参照する§5.4.3.3の最初の箇条書きは、宣言型がinterfaceである場合にメソッドの解決が無条件に失敗すると述べていますが、とにかく意味がありません。無条件に参照されているため、つまりinvokestaticのドキュメントには、インターフェイスメソッドに別のルックアップアルゴリズムを使用する必要がある旨が記載されていません。staticメソッドをinterfaceとして呼び出すことは一般的に不可能です。

これは明らかに、staticメソッドの明示的に追加された機能をinterfaceに組み込んだ仕様の意図ではありませんが、もちろんこれも呼び出し可能でなければなりません。あなたの例では

それはCONSTANT_Methodref_info代わりのCONSTANT_InterfaceMethodref_infoを経由してインターフェイスメソッドを参照するよう宣言クラスが変更された後、呼び出し元のクラスは確かに、すなわち§4.4.2、仕様に違反しません。しかし、invokestatic命令のドキュメントの現在の状態では、定数プールアイテムのタイプ(実際の意図かもしれないが、そこにはない)に基づいて動作を変更するよう強制されていません。そして、言い換えれば、現在の文言を守ることは、interfaceにあるinvokestaticを拒絶することを意味する。

仕様では別のクリーンアップが必要ですが、Methodref_infoInterfaceMethodref_infoの区別は、Java 8よりもはるかに有用ではありません(上記の回答と比較してください)。究極の修正は、将来的には区別を取り除くことになります。

関連する問題