プログラマ38の日記

主にプログラムメモです。

Java: 外部ライブラリの定数を参照する時は静的コンパイルに気をつけよう

他のライブラリを参照して開発する時に、参照先の定数を使うときがあります。

例えば、WebServiceWSDLを展開したライブラリで、WebSerivceのendpointは定数として定義されていて、その定数を使いたい時があります。

 

開発しているプログラムでその定数を使う時に注意することのメモです。 

結論としては、Javaの静的コンパイルを正しく理解した上で開発する必要があります。

 

例えば、次のようなライブラリがあります。

[Test2.jar]
public class Test2 {
  
  public static final String str1 = "string1";
  public static final String str2 = "string2";

  public static String getStr2(){
    return str2;
  }
}

そして次のプログラムで上記のTest2.jarを呼び出します。

public class Test1 {

  public static void main(String[] args) throws Exception{
    System.out.println(Test2.str1);
    System.out.println(Test2.getStr2());
  }
}

実行結果は、当たり前ですが
string1
string2
となります。

 

ですが、この後Test2.jarを次のように更新します。

[Test2.jar]
public class Test2 {
  
  public static final String str1 = "update:string1";
  public static final String str2 = "update:string2";

  public static String getStr2(){
    return str2;
  }
}

そして先ほどのTest1を再度コンパイルし直さないで実行すると実行結果は、

string1
update:string2
となります。

これはJavaの静的コンパイルという仕組みで、定数をプログラムで使用するとコンパイル時に定数が文字列として展開されるからになります。

 

参照先のライブラリの定数が変更される可能性がある場合、参照先の定数を使う箇所は次のような対応が考えられます。

①参照先のライブラリが更新されたら、常に参照元コンパイルし直す。

②参照先のライブラリの定数を参照しないよう参照先でgetterを用意してもらい、getterを使用する。

③参照先のライブラリの定数を静的コンパイルされないようリフレクションで定数を取得する。
リフレクションの例  Test2.class.getField("str1").get(null);

 

①は確実です。ちゃんとantでビルドすれば問題ないですが、手間がかかります。(複数のjarが混在しているとなかなか大変になります)

②は参照先のライブラリの開発側に対応を依頼することになります。対応してくれればいいですが、何らかの要因で頼めない場合はこの方法はとれません。

③は最後の手段ですが、①の手段以外で、どうしても静的コンパイルを回避したいならこの方法になります。

最後に

定数って静的コンパイルされるんだよなーというのがわかっていれば事前に対応しておく、もしくは運用を考えておくということができますが、後から問題に直面すると原因特定に時間がかかってしまうものだと思います。