2012-05-25
Java デフォルト・ロケールの上書き実行
※ 以下の内容は Windows だけでなく 他の OS でも有効かもしれない。
JVM や Java コンパイラ は、
では、オプションを指定しないときに適用されるデフォルトの表示言語を切り替えることは可能だろうか。
言語はロケールの一部として扱われており、Windows では、
デフォルトロケールは、システム・ロケールの設定値となる。
問題なのは、Windows OS のグレードによっては言語が設定できないことだ。
言語が設定できないグレードの Windows で、無理やりレジストリの設定を変えてみたが、
Java 7 では言語を切り替えられなかった。
また、システムロケールは、ディスクトップ環境のすべてのアプリケーションに影響するので、
Java 環境だけ言語を切り替えられる方法があると便利かもしれない。
それで、いろいろと試行錯誤した結果、下記の方法に行き着いた。
以下の方法はパラメータの指定の仕方を変えれば
まずは、シンプルな方法の手順を示す。
この方法は、ユーザがコマンドプロンプトなどで java コマンドを発行する場合に適用されるデフォルトの言語を切り替えることができる。
他のプログラムが直接、
- 以下のバッチファイル
java.bat を作成し、C:\JAVA_BOOT に格納する。@echo off %JAVA_HOME%\bin\java.exe -Duser.language=en -Duser.country=us %*
- 環境変数
PATH の先頭にC:\JAVA_BOOT; を設定する。
次は、他のプログラムから起動されたときでも、デフォルト言語を切り替える方法である。
たとえば、Maven が
%JAVA_HOME%\bin のjava.exe の名前をjava_.exe に変更する。- 以下のバッチファイルを
HOGE.BAT の名前で作成し、%JAVA_HOME%\bin に格納する。@echo off java_.exe -Duser.language=en -Duser.country=us %*
- 以下のC言語プログラムを作成し、実行ファイルの名前を
java.exe としてコンパイルする。コンパイルされた実行ファイルを%JAVA_HOME%\bin に格納する。#include <windows.h> #include <tchar.h> #include <string.h> #include <memory.h> #include <stdio.h> #include <assert.h> #define JAVA_EXE_LEN 8 #define JAVA_LEN 4 #define BATCH_FILE_NAME_SHORT _T("HOGE") #define BATCH_FILE_NAME_LONG _T("HOGE.BAT") int _tmain(int argc, _TCHAR* argv[]) { assert( _tcslen(BATCH_FILE_NAME_SHORT) == JAVA_LEN ); assert( _tcslen(BATCH_FILE_NAME_LONG) == JAVA_EXE_LEN ); _TCHAR* batch_file_name = BATCH_FILE_NAME_SHORT; _TCHAR* pCmd = GetCommandLine(); size_t pgm_path_len = _tcslen(argv[0]); size_t copy_len = JAVA_LEN; _TCHAR* dest = pCmd + pgm_path_len - JAVA_LEN; if (pgm_path_len >= JAVA_EXE_LEN) { _TCHAR* s = argv[0] + pgm_path_len - JAVA_EXE_LEN; if ( _tcscmp( _T("java.exe"), s ) == 0 ) { dest = pCmd + pgm_path_len - JAVA_EXE_LEN; batch_file_name = BATCH_FILE_NAME_LONG; copy_len = JAVA_EXE_LEN; } } if (*pCmd == '"') dest += 2; memcpy(dest, batch_file_name, sizeof(_TCHAR) * copy_len); _tsystem(dest); return 0; }
%JAVA_HOME%\jre\bin のディレクトリが存在し、java.exe がある場合、同様に上記 1 〜 3 の処理を行う。
おそらく、このソースコードは MinGW と VC の両方でコンパイルできると思う。
なお、環境変数 PATH の設定について、一度、
java.exe の実行パスに環境変数 PATH の設定が反映されない(Windows) - happynowの日記
を参照してほしい。
もっとスマートな方法があれば、どなたかご教示頂きたく。
2012-05-21
java.exe の実行パスに環境変数 PATH の設定が反映されない(Windows)
したがって、下のように PATH の設定で
PATH=...;%SystemRoot%\system32;...%JAVA_HOME%\bin;
対策としては
ところで、ある掲示板で次のような書き込みを見つけたが、最近のバージョンの Java については、この説明は不十分である。
まず「カレントバージョンのjava.exeを呼び出すだけ」とあるが、この実行形式ファイルはレジストリを参照して、カレントバージョンのjava.exeを呼び出すだけの動作を行っているようでした。(レジストリを書き換えると実行されるバージョンが変わる)
Javaの道 — 掲示板(パスについて)
C:\Java\jre7\bin\java.exe と C:\Windows\System32\java.exe を比較しています...
ファイルに違いはありません
また動作については、バージョン 1.7 を調べたところ以下のような仕様であった。
C:\Windows\System32\java.exe の実行ファイルにはバージョン番号が埋め込まれている。- 実行時、その埋め込まれているバージョン番号と、レジストリの
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\CurrentVersion
の値が比較され、両者のバージョン番号が異なる場合、エラーとなり実行が中断される。 - 一致している場合、そのバージョンの JVM を起動する。
という動作らしい。
バイナリエディタで
2012-05-13
ArrayList の初期化イディオム
Java言語で固定要素のListを初期化する際のイディオム - 達人プログラマーを目指して
List<String> list = Arrays.asList("data1", "data2", "data3");
だと、add()やremove()などの要素の追加削除はできないそうだ。
同エントリのブクマコメントより。
かっこいいわ〜。僕はArrays.asListつかわないでインスタンスイニシャライザ使うな。
List<String> list = new ArrayList<String>(){{add("hoge");add("fuga");}};
ikeike443 2010/12/26
The Principle of Truth in Advertising および Principle of Indecent Exposure
Java では、配列よりも、List などのコレクション・オブジェクトを使用する方がメリットが遥かに大きい。
特にジェネリック型の配列は、発見しにくいバグも生みやすく、出来るだけ使用を避けるべきである。
ジェネリック型の配列を使わない、これが大原則である。
しかし、何かしらの理由でジェネリック型の配列を使わざるを得ないときには、
「広告の真実性」と「露出禁止」の2つの原則を押さえておくとよい。
この2つの原則は「Java Generics and Collections」で説明されているものである。
以下では、配列は参照型配列を意味し、基本データ型の配列は扱わない。
これは、参照型の配列は共変であるが、基本データ型の配列はそうではないことに関係する。
1つ目の原則は The Principle of Truth in Advertising (広告の真実性の原則)である。
The Principle of Truth in Advertising:
the reified type of an array must be a subtype of the erasure of its static type.訳)広告の真実性の原則
Java Generics and Collection — 6.5 The Principle of Truth in Advertising
配列の具象型は、その静的な型に対するイレイジャのサブタイプでなくてはならない。
この「広告の真実性の原則」を一言で言えば、「見かけ上の型は、実際の型と不整合があってはなりませんよ」ということ。
そんな難しいことを言っているわけではない。
ジェネリック導入前なら、この原則は次のように言われるだろう。
配列の変数および引数、戻り値の型は、配列の実際の型のスーパータイプでなければならない。
言い換えれば、
配列の実際の型は、配列の変数および引数、戻り値の型のサブタイプでなければならない。
ダウンキャストしなければ、これはコンパイラによって保証されるが、
このルールを守らずダウンキャストするなら、実行時エラーが発生する。
以下のコードを例とすれば、3行目の変数の型は Double であり、配列の具象型 Integer のスーパータイプではない。
これは原則に違反しており、このコードは、実行時に ClassCastException を引き起こす。
Integer[] ints = new Integer[] { 1, 2, 3 }; Number[] nums = ints; Double[] dbls = (Double[]) nums; // ClassCastException
この簡単なルールを、ジェネリック型システムに適応させたものが The Principle of Truth in Advertising である。
では、原則の説明を詳しくみていく。上の引用文を再掲すると、
The Principle of Truth in Advertising:
the reified type of an array must be a subtype of the erasure of its static type.訳)広告の真実性の原則
Java Generics and Collection — 6.5 The Principle of Truth in Advertising
配列の具象型は、その静的な型に対するイレイジャのサブタイプでなくてはならない。
用語の意味であるが、具象型(reified type)とは実行時の実際の型のことである。
詳細は次の記事を参照して欲しい。
具象化可能型(reifiable type)
静的な型(static type)とは、変数の宣言された型や式の型のことだと思ってよい。
コンパイル時の型 (compile-time type) とも呼ばれる。
さて、文全体の解釈であるが、元の文を次のように言い換えてみる。
配列の具象型(実際の型)は、変数や引数、戻り値の静的な型に対して、それらのイレイジャのサブタイプでなければならない。
逆にも言い換えてみる。
配列の変数や引数、戻り値の静的な型は、そのイレイジャが、配列の具象型のスーパータイプでなければならない。
例を挙げると
public static void main(String[] args) { List<Integer> ints = Arrays.asList(1, 2, 3); Collection<Integer>[] colls = (List<Integer>[]) new List<?>[]{ints}; for(Collection<Integer> coll: colls) for(Integer i: coll) System.out.println(i); Set<Integer>[] sets = (Set<Integer>[]) colls; // ClassCastException }
3行目の代入式では、型変換が2度行われている。
まずは List<?>[] から List<Integer>[] の明示的な変換である。
変換対象の配列の具象型は List<?>[] である。そして List<Integer>[] のイレイジャは、List[] である。
と書いてある通り、List<?>[] は List[] のサブタイプである。したがって、この変換は妥当である。Every parameterized type is a subtype of the corresponding raw type.
Java Generics and Collection — 5.3 Generic Library with Legacy Client
続いて、代入式の右辺から左辺への List<Integer>[] から Collection<Integer>[] への暗黙的な型変換が行われるが、
確認すべきは、配列の具象型 List<?>[] と Collection<Integer>[] との関係が原則に従っているか否かである。
これは、Collection<Integer>[] は List<Integer>[] のスーパータイプであることから、結果的に
配列の具象型のスーパータイプとなっていることがわかる。よって、この代入式は妥当である。
最後の代入式であるが、これは「広告の真実性の原則」を破っている。
配列の具象型は List<?>[] であるが、Set<Integer>[] のイレイジャ Set[] と互換性がない。
結果、この代入では、ClassCastException がスローされる。
さて、この原則はプログラムが全体として問題なく動作することの必要条件であって、
コーディングの指針を言っているわけではない事に注意してほしい。
本書では、広告の真実性の原則を説明するのに次のコードを例としている。
import java.util.*; class Wrong { public static <T> T[] toArray(Collection<T> c) { T[] a = (T[]) new Object[c.size()]; // unchecked cast int i = 0; for (T x : c) a[i++] = x; return a; } public static void main(String[] args) { List<String> strings = Arrays.asList("one", "two"); String[] a = toArray(strings); // class cast error } }
下から3行目の class cast error の原因を特定するため、本書では、コンパイラによって
生成されたイレイジャによる実装を確認している。以下がその実装である。
import java.util.*; class Wrong { public static Object[] toArray(Collection c) { Object[] a = (Object[])new Object[c.size()]; // unchecked cast int i=0; for (Object x : c) a[i++] = x; return a; } public static void main(String[] args) { List strings = Arrays.asList(args); String[] a = (String[])toArray(strings); // class cast error } }
このエラーを本書は以下のように説明する。
原則が破られているのは確かにクライアント側(呼び出し側)であるが、The principle is obeyed within the body of toArray itself, where the erasure of T is Object, but not within the main method, where T has been bound to String but the reified type of the array is still Object.
訳)toArray の本体内では T のイレイジャは、Object であるで原則に従っています。しかし、main メソッドでは、T が String にバインドされますが、配列の具象型が Object であることにより、原則が破られています。
Java Generics and Collection — 6.6 The Principle of Truth in Advertising
未検査警告を黙殺したのはライブラリ(呼び出され側)であって、クライアントは無実である。
ライブラリの設計に問題がある。
さて、次の露出禁止の原則である。
とあるが、むしろ、次の記述の方が主体がはっきりしていてわかりやすい。Principle of Indecent Exposure:
never publicly expose an array where the components do not have a reifiable type.
Java Generics and Collection — 6.6 Principle of Indecent Exposure (P.88)
In particular, a library should never publicly expose an array with a nonreifiable type.
訳)特に、ライブラリは具象化可能型ではない型を持つ配列を公開するべきではありません。
Java Generics and Collection — 6.6 Principle of Indecent Exposure (P.86)
以下の例は、この原則に違反しており ListHolder が具象化可能型でない配列 List<Integer>[] を返している。
そのため、このコードを実行すると、main の最後の行で ClassCastException がスローされる。
これもライブラリの設計が悪いため、コンパイル時、main 側は、型変換については、何も警告されない。
import java.util.Arrays; import java.util.List; class ListHolder { @SuppressWarnings("unchecked") List<Integer>[] ints = (List<Integer>[]) new List<?>[5]; // unchecked cast public List<Integer>[] getLists() { return ints; } } public class ListHolderClient { public static void main(String[] args) { ListHolder holder = new ListHolder(); List<?>[] lists = holder.getLists(); lists[0] = Arrays.asList("a"); List<Integer> ints = holder.getLists()[0]; int i = ints.get(0); // ClassCastException } }
広告の真実性の原則は、配列オブジェクトの実際の型となる具象型と、静的な型の実行時の姿であるイレイジャとの整合性について述べており、どちらかというと実行時のルールである。一方、露出禁止の原則は、型宣言について述べており静的な型についてのルールである。これらの特徴を本書では次のように説明する。
The Principle of Truth in Advertising and the Principle of Indecent Exposure are closely linked. The first requires that the run-time type of an array is properly reified, and the second requires that the compile-time type of an array must be reifiable.
訳)広告の真実および露出禁止の原則は、密接な繋がりがあります。前者は配列の実行時の型が適切に具象化されている必要があり、後者は、配列のコンパイル時の型が具象化可能型あることを要求しています。
Java Generics and Collection — 6.6 Principle of Indecent Exposure (P.88)
これらの原則についてはこちらも参考になる。
普通の(業務)Javaアプリケーションでは配列をなるべく使用しない方がよい - 達人プログラマーを目指して
2012-05-12
内部クラスからアクセス可能なローカル変数が final でなければならない理由
なぜ、内部クラス(ローカルクラス、匿名クラス)からアクセス可能なローカル変数が final でなければならないのか、
その理由が次の記事に説明されている。
いまさらだけど、Java言語にはクロージャーがない - 達人プログラマーを目指して
しかし、
つまり、以下のプログラムを考えてみれば内部クラス中からアクセスするローカル変数がfinalでなくてはならない理由が納得ができます。
とあるが、残念ながら、例示されているプログラムでは納得することはできない。
以下がそのプログラム。
public Test createTest(final int param) { final String value = "Hello"; // finalでないと // value = "Hello2"; // param = 3; // などの再代入が可能で結果が矛盾する。 return new Test() { public void print() { System.out.println("param = " + param); System.out.println("value = " + value); } }; }
このサンプルプログラムは return 文でインスタンス生成しているので、
インスタンス生成時以降の値の変更を防いでいるということが見えにくい。
それを見るには、以下のような例の方がよい。
public Test createTest(final int param) { final String value = "Hello"; Test test = new Test() { public void print() { System.out.println("param = " + param); // 値はコピーされる System.out.println("value = " + value); } }; // param = 3; ← このような再代入を防ぐ // value = "Bye"; ← このような再代入は防ぐ return test; }
リンク先で参照された記事を、ここでも紹介しておく。
JavaFAQ: 内部クラス - inner class
[S016 Q-14]
このリンク先の記事で、final変数へ制限する理由が説明されている。
この記事では、内部クラスのインスタンスを生成する時、ローカル変数の値をコピーしてインスタンス内部に保持するのは、スタック上のローカル変数が破棄された後でも、それらのローカル変数の値を使えるようにするため、だと書いており、このコピーした値が矛盾のないものとするため「参照可能なものを内部クラスが生成される時点以降に値が変更されないもの、すなわち、final宣言されているローカル変数や引数のみに制限し、」と述べている。
引用文の「内部クラスが生成される時点以降に値が変更されないもの、」がポイントである。
それで、上記のようにサンプルプログラムを変えてみたわけである。
それでも、疑い深い人は、
「じゃ、メソッドからリターンする時点での最終的な値を Test クラスのインスタンスに埋め込めばいいではないか」
と考えるかもしれない。
しかし、内部クラスのインスタンスは、当該のメソッドのなかで使用される可能性がある。
上の2番目のサンプルプログラムでいえば、Test クラスのインスタンスが生成された直後に
test.print(); とメソッドを呼び出すことが考えられる。
そのような可能性を考えれば、値のコピーは、内部クラスのインスタンス生成時点で行うのが妥当といえる。
コマンドプロンプト %〜% のエスケープ
コマンドライン・プロセッサは、入力文字列に対して、
検索文字列: %環境変数%
置換文字列: 環境変数の値
という置換処理を行う。
したがって %〜% は 〜 に対応する環境変数があれば、
%〜% は、その環境変数の内容で置換され、
なければ %〜% という文字列がそのまま残る。
%〜% や %1 などの置換処理後は % はリテラルとして扱われ & | 空白などの特殊な意味を持たない。
次の使用例を見ると %〜% のペアは前方から処理されることがわかる。
SystemRoot=C:\Windows
C:\>echo %SystemRoot%SystemRoot%
C:\WindowsSystemRoot%
この性質を利用すれば以下のように、
%環境変数% をそのままプログラムに渡すことが出来る。
C:\>set ZZZ
ZZZ=%
C:\>echo %ZZZ%SystemRoot%
%SystemRoot%
コマンド ラインにカレット (^) 文字があると、その直後の文字は制御文字としてではなくリテラル文字として解釈されます。したがって、引用符 (")、スペース、先頭のスラッシュ、カレット、その他の任意のリテラル文字をパラメータまたはスイッチの値に直接埋め込むことができます。ただし、スイッチ名には埋め込むことができません。次に例を示します。
>Edit.Find ^^t /regex
カレットは、引用符の前後のどちらに置かれた場合でも同じ働きをします。行の最後の文字がカレットの場合は無視されます。
http://msdn.microsoft.com/ja-jp/library/aa301845(v=VS.71).aspx
キャレットは基本的にはキャレットの直後の制御文字に作用し、% には直接作用しない。
キャレットの処理は環境変数の置換の後に行われるようなので、
% の直後にキャレットを置くことによって結果的に % をエスケープ出来る。
たとえば、%SystemRoot% をそのまま出力したいときには、
%^SystemRoot% とすると、^SystemRoot という環境変数が見つからないため
結果、%SystemOut% という文字が出力される。
%SystemRoot%
2012-05-09
やはり T.class や new T() をしなくてもよい。
Nagise氏の「ぶっちゃけるとT.class やnew Tしたいケースは設計が悪いのだと思う。」という言葉の通りなのか、
Javaのジェネリクスで,T.class や new T() ができず悩んだ話 (型パラメータのインスタンス化に関し、フレームワーク設計からケーススタディ) - 主に言語とシステム開発に関して
の記事で取り上げられているフレームワークの設計には、おかしなところがある。
当記事には
その共通の性質は,BaseEntityに記述される。
とあるが、この点がおかしい。
この共通の性質というのは実はデータベースに関わる物理情報を管理するものである。
通常の DAO(Data Access Object)パターンでは、物理情報はDAOオブジェクトが管理する。
しかし、問題のフレームワークでは、データベースに関わる物理情報が
ビジネスオブジェクトであるエンティティクラスで管理されている。
(エンティティクラス http://code.google.com/p/.../Friend.java の logicalFromPhysical, toPhysicalEntity, tableName(), columns() などのメソッドがそれである。logicalFromPhysical をやるのが DAOオブジェクトではないか)
そこを直せば、以下のような素直な実装で、(裏技的に型引数を取得することなく、)要件が満たせるはず。
このサンプルコードは簡略化しているが大体のロジックは掴めると思う。
// Main.java import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { FriendDAO fd = new FriendDAO(); Friend friend = fd.findById(1); // FriendSub sub = fd.findById(1); // compile-time error System.out.println(friend); } } interface BaseEntity {} class Friend implements BaseEntity {} class FriendSub extends Friend {} abstract class BaseDAO<T extends BaseEntity> { abstract String tableName(); abstract String[] columns(); abstract T logicalFromPhysical(Cursor c); public T findById(int id) { List<T> result = new Finder<T>() .where("id = " + id) .findAll(this); if (result.size() > 0) { return result.get(0); } return null; } } class FriendDAO extends BaseDAO<Friend> { @Override public String tableName() { return "friends"; } @Override public final String[] columns() { return new String[] { "id", "name", "age", "favorite_flag" }; } @Override Friend logicalFromPhysical(Cursor c) { return new Friend(); } } class Finder<T extends BaseEntity> { Database db = new Database(); String selection; public Finder<T> where(String selection) { this.selection = selection; return this; } public List<T> findAll(BaseDAO<T> dao) { List<T> result_list = new ArrayList<T>(); Cursor c = db.query( dao.tableName(), dao.columns(), selection); while (c.moveToNext()) { T entity = dao.logicalFromPhysical(c); result_list.add(entity); } c.close(); return result_list; } } class Database { Cursor query(Object... o) { return new Cursor(); } } class Cursor { int i = 0; boolean moveToNext() {return i++ != 1;}; void close() {} }