コンパイル時に文字列定数を決定できる場合、Javaはインライン文字列定数を保証されますか
質問
この場合を考慮してください:
public Class1 {
public static final String ONE = "ABC";
public static final String TWO = "DEF";
}
public Class2 {
public void someMethod() {
System.out.println(Class1.ONE + Class1.TWO);
}
}
通常、コンパイラーはONEおよびTWO定数をインライン化することを期待します。ただし、この動作は保証されていますか?クラスパスにClass1なしで実行時にClass2をデプロイし、コンパイラに関係なく動作することを期待できますか、またはこれはオプションのコンパイラ最適化ですか?
編集:一体なぜこれを行うのですか?さて、アプリケーションの両端(RMIを介したクライアントとサーバー)で共有される定数があり、この特定のケースでは、その境界の片側にしか存在できないクラスに定数を配置すると非常に便利です(論理的にはその定数値を所有するものであるため)、コードの両側で共有する必要があるという理由だけで、任意の定数クラスに保持するのではありません。コンパイル時にはソースファイルのすべてが1セットですが、ビルド時にはパッケージごとに分割されます。
解決
定数式として扱われることが保証され、 JLSのセクション15.28 :
コンパイル時の定数式は の値を表す式 プリミティブ型またはそうする文字列 突然完了せず、構成されている 次のみを使用します。
- プリミティブ型のリテラルとString型のリテラル(§ 3.10.5)
- プリミティブ型にキャストし、String型にキャストします
- 単項演算子+、-、〜、および! (ただし、++または-ではありません)
- 乗法演算子*、/、および%
- 加算演算子+および-
- ...
...
String型のコンパイル時定数 常に「抑留」されている共有するために メソッドを使用した一意のインスタンス String.intern。
今では、インライン化が保証されているとは言えません。ただし、仕様のセクション13.1では次のように記載されています。
定数であるフィールドへの参照 変数(§ 4.12.4)はで解決されます コンパイル時間を定数値に それが示されています。そのような言及はありません 定数フィールドは バイナリファイル内のコード(を除く を含むクラスまたはインターフェース コードを持つ定数フィールド 初期化する)、およびそのような定数 フィールドは常に表示されている必要があります 初期化済み;デフォルトの初期値 そのようなフィールドのタイプには 観察されることはありません。
言い換えれば、式自体が定数でない場合でも、 Class1
への参照はないはずです。はい、あなたは大丈夫です。これは、連結された値がバイトコードで使用されることを必ずしも保証するものではありませんが、前述のビットは連結された値がインターンされることを保証するため、驚いたことになります 連結された値をインライン化しなかった場合。動作しない場合でも、 Class1
がなくても動作することが保証されています。
他のヒント
javac 1.6.0_14でコンパイルすると、次のバイトコードが生成されます。
public void someMethod();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String ABCDEF
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
したがって、文字列はコンパイル時に連結され、結果はClass2の定数プールに含まれます。
コンパイラによってインライン化されるのではなく、実行時にインタプリタによってインライン化され、可能であればアセンブリコードに変換されます。
すべてのインタープリター(JVM)が同じように機能するわけではないため、保証できません。ただし、最も重要な実装で十分です。
残念ながら、これを維持するためのリンクがありません:(
疑わしいが、確かに、これが機能するかどうかはわからないが、良いアイデアのようには聞こえない。
「通常」これを行う方法は次のとおりです。
- クライアントとサーバー間で共有されるパッケージに定数を入れます。おそらく、そのようなパッケージがあるのは、それがインターフェースの行き先だからです。
- そのようなパッケージがない場合は、共有定数を持つ2つのクラスを作成します。1つはサーバー用、もう1つはクライアント用です。
JLS 13.4.9 をご覧ください。 。コンパイラーが定数をインライン化することを明示的に要求するわけではありませんが、条件付きコンパイルと switch
ステートメントでの定数のサポートにより、コンパイラーは常に定数をインライン化することを示唆しています。
enum
に組み込まれている独自のバージョンの機能をコーディングしているようです。 public static final
を行い、 name( )
および toString()
(他にもいくつかの利点がありますが、メモリフットプリントが大きくなるという欠点がある可能性があります)。
enumがまだ含まれていない古いバージョンのJavaを使用していますか?