前の関連記事:linuxBean14.04(36)失敗したLibreOfficeSDKの例を再make:その3
今度は/opt/libreofficedev5.0/sdk/examples/DevelopersGuide/Formsにある例です。大量の非推奨APIの注意とともに「SalesFilter.javaの操作は、未チェックまたは安全ではありません。」という注意もでてきます。(結論としては動作に支障はないので修正しませんでした。)
注意:SalesFilter.javaの操作は、未チェックまたは安全ではありません。
Java5で実装されたジェネリクスという型指定に関する仕組みの影響だそうです。(コンパイル時警告に注意 - プログラマーの脳みそ)
-Xlint:uncheckedオプションを指定して再コンパイルしてみよ、ということなのでやってみます。
linuxBean14.04(33)失敗したLibreOfficeSDKの例を再make:その1と同様にしてremakeでSalesFilter.classがターゲットになっているルールにたどり着きます。
/opt/libreofficedev5.0/sdk/examples/DevelopersGuide/Forms/MakefileをGeanyで開きます。
ビルド→setsdkenv。
make clean
remake --debugger
info targets
Makefile:107:
/home/pq/libreoffice5.0_sdk/LINUXexample.out/class/FormsExamples/common/SalesFilter.class
break /home/pq/libreoffice5.0_sdk/LINUXexample.out/class/FormsExamples/common/SalesFilter.class
うーん、SalesFilter.javaはまだコンパイルされていないはずなのですがSalesFilter.javaの注意がでてきますねぇ。
コンパイルされなくてもjavacの引数になった時点で検査されるようです。
target
# commands to execute (from `Makefile', line 108): -$(MKDIR) $(subst /,$(PS),$(@D)) $(SDK_JAVAC) $(JAVAC_FLAGS) -classpath "$(SDK_CLASSPATH)" -d $(COMMON_CLASS_OUT) $^コマンドはこのようになっています。
expand SDK_JAVAC
../../../settings/std.mk:74 (origin: makefile) SDK_JAVAC := "/usr/bin/javac"SDK_JAVACがjavacであることがわかります。
なのでJAVAC_FLAGSに-Xlint:uncheckedを入れればよいことがわかります。
expand JAVAC_FLAGS
../../../settings/settings.mk:35 (origin: makefile) JAVAC_FLAGS :=$(JAVAC_FLAGS)はいまのところ何も入っていません。
eval JAVAC_FLAGS=-Xlint:unchecked
EvalコマンドでJAVAC_FLAGSに-Xlint:uncheckedを代入します。
Shellコマンドではうまくできませんでした。
expand JAVAC_FLAGS
(origin makefile) JAVAC_FLAGS := -Xlint:uncheckedちゃんと代入されていますね。
nextコマンドを5回繰り返すと-Xlint:uncheckedオプションの結果が表示されました。
SalesFilter.java:136: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( null ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:139: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( aNowAndHere ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:145: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( aCalendar.getTime() ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:150: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( aCalendar.getTime() ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:155: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( aCalendar.getTime() ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:158: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( null ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:299: 警告: [unchecked] raw型ArrayListのメンバーとしてのset(int,E)への無検査呼出しです m_aFilterDates.set( m_nPreviousFilterIndex, aDate ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:367: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです aFilterItems.add( aOldFilterItems[i] ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:393: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです aFilterItems.add( aDate.toString() ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します SalesFilter.java:394: 警告: [unchecked] raw型ArrayListのメンバーとしてのadd(E)への無検査呼出しです m_aFilterDates.add( aDate ); ^ Eが型変数の場合: クラス ArrayListで宣言されているEはObjectを拡張します 注意:一部の入力ファイルは非推奨のAPIを使用またはオーバーライドしています。 注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。 警告10個全部配列へ要素の追加しているところですね。
NetBeansでSalesFilter.javaを開く
まず豆ボタン→アクセサリ→ファイルマネージャ PCManFM (root)で/opt/libreofficedev5.0/sdk/examples/DevelopersGuideにあるFormsフォルダをコピーしてForms.originalという別名で保存してバックアップしておきます。
元のFormsフォルダのパーミッションの「内容の変更」を「すべて」にしておきます。
「はい」。
linuxBean14.04(35)FirstStepsの例をNetBeans8でビルドすると同じようにしてNetBeansでプロジェクトを作ります。
プロジェクト名はFormsExamplesにしました。
ライブラリは「LibreOfficeDev5.0」でだけで済みました。
早速SalesFilter.javaを開いてみます。
「無検査呼出しです」といわれている箇所には全く何もそれを知らせてくれるものがついていません。
ジェネリクスについては全く知らなかったでちょっと勉強してみました。
ジェネリックスの基本 | Javaコード入門
Javaジェネリクス再入門 - プログラマーの脳みそ早速SalesFilter.javaを開いてみます。
「無検査呼出しです」といわれている箇所には全く何もそれを知らせてくれるものがついていません。
ジェネリクスに対応していないコードをジェネリクスに対応させるのは大変らしい
ジェネリクスについては全く知らなかったでちょっと勉強してみました。
ジェネリックスの基本 | Javaコード入門
Java総称型メモ(Hishidama's Java Generics Memo)
いちばん最後のサイトには参照ページも載っていていろいろ知ることができました。
ジェネリクス(総称型)とはJava5(JDK1.5)から導入された仕組みで、それまでは値を取り出すときに型チェックを行っていたものを、値を代入するときに型チェックするようにしたもののようです。
これによって、実行時までわからなかった型の不一致がコンパイル時にわかるようになったそうです。
Javaはすごく「型」にうるさそうな印象があったのですが、Java4までは型を指定せずにListなどの要素が追加できたということは意外でした。
もうすでに広く普及してしまったJava仮想マシン(ランタイム)をいまさらジェネリクスに対応させるわけにはいかないので、ジェネリクスはコンパイラにより従来と変わりがないジェネリクスがないバイトコードにコンパイルされます。(このジェネリクスの型情報を消去することをErasureイレイジャ、という)(まとめ)
Javaには複数の値を収納するものとして配列とコレクションがありますが、型指定という観点から見た場合配列とコレクションは全く仕組みが違い、配列は継承した型の要素も追加可能であるcovariant(共変)であるのに対しコレクションは共変ではなく、継承している型であっても要素に追加できません。(Genericはcovariantではない)
共変配列は、型保証されないにもかかわらずJavaに導入されました。共変配列を使用することで、任意の型の配列に対して1つの操作を適用できるためです。配列が共変なのは、これまでJavaにジェネリクスの仕組みがなかったための妥協の産物であったそうです。(なぜ Java の配列は共変で、Generics は共変ではないのか - sinsengumi血風録のコメント。)
JAVA-MJ12-Architect-OpenJDK.pdf
配列にジェネリクスで型指定してもコンパイルするとジェネリクスの型指定は消去されてしまうので、配列ではジェネリクスは使えません。(Covariantにまつわる、さらなるトラブル)
ジェネリクス自体もこれまでの型指定がなかったコレクションとの互換性を保つためにいろいろ制約があるようです。(未踏の道)
既存のライブラリー・クラスがgenericとスムーズに動作するように変換するのは、それほど簡単なことではありません。Javaの理論と実践: Generics、了解!の記事はジェネリクスが導入された経緯がよくわかって面白かったのですが、Java4までに書かれたコードをジェネリクスに対応されたコードに書き直す気がすっかり萎えてしまいました。
既存のクラスをgeneric化する
別に書き直さなくてもmakeしたものを実行するとちゃんと動きますし、、、
Java8ではラムダ式の導入がされている
java - Generic data type conversion method - Stack Overflowを読むとJava8ではジェネリクスへの対応が容易になっていそうに思ったのでJava8について調べてみました。
Java新機能メモ(Hishidama's Java up-to-date)にあるリンクにわかりやすい解説がありました。
【祝】Java SE 8 正式リリース | 寺田 佳央 - Yoshio Teradaの2つ目のスライドがわかりやすいです。
ラムダ式についての解説は48枚目から始まります。
53枚目からはStream APIについての解説があり、これを使うことによってif文やfor文をラムダ式で置き換えることができるそうです。
67枚目にはJava8に乗り換える心構えが記されています。
ラムダ式はPythonでもでてきました。(6. 式 (expression) — Python 3.3.6 ドキュメント)
そもそも「ラムダ式」とは何なのか、ラムダ計算 - Wikipediaを読んでもわからなかったので、コンピュータの本を2冊購入で購入した「C言語による計算の理論」を見てみると166ページにラムダ式の定義がわかりやすく書いてありました。
たとえば$$$\left( a^{2}+3y\right) $$$と書いてあった場合、これはひとつの値なのか、関数なのか、また関数としてもaを変数とするのか、yを変数とするのか、曖昧である。これに対して、ギリシャ文字の$$$\lambda$$$(ラムダ)を使って関数を明確に記述する方法が知られている。たとえば$$\lambda a.\left( a^{2}+3y\right) $$と書けば、これはaを変数とする関数、つまり入力値aに対して値$$$\left( a^{2}+3y\right) $$$を出力する関数(パラメータyの値ごとに決まる関数)ということになる。この表記をラムダ記法という。ラムダ記法では式の前に$$$\lambda$$$の前に変数を書き、変数に代入する値を書きたいときはその値を最後に書き加えます。$$\left( \lambda a.\left( a^{2}+3y\right) \right) \left( 3\right) =3^{2}+3y$$
$$$\lambda a.\left( a^{2}+3y\right) $$$はPythonだとこんな感じになりますね。
lambda a: a**2 + 3 * yJava8ではa -> Math.pow(a, 2)+3 * y でいけそうな感じもしますけど、型の指定が略せるのかどうかよくわかりませんでした。(コンテクストによる?)(ラムダ式・Stream APIの理解のポイントは「型」 - Java EE 事始め!)
Java8では日時APIが変更されている
SalesFilter.javaの128行目から130行目に非推奨APIを見つけました。
java.util.Dateのメソッドです。
Javadocを読むとCalendar setに変更されたと書いてあります。
ところが【祝】Java SE 8 正式リリース | 寺田 佳央 - Yoshio Teradaの1つ目のスライドの64枚目に、このCalendarクラスがさらにJava.timeパッケージに置き換わるようなことが書いてあることに気がつきました。
ここが大変だよJava 8 Date-Time API(1):ImmutableでスレッドセーフになったJavaの新しい日時APIの基礎知識 (1/5) - @IT
もう変更点が多くなりすぎるのでもう修正するのはあきらめることにしました。
参考にしたサイト
コンパイル時警告に注意 - プログラマーの脳みそ
Java5から導入されたジェネリクスという仕組みによって型指定の方法が変わりました。
Debugger Command Reference - GNU Make Debugger, Remake
Makeのデバッガのコマンドリファレンス。
Javaジェネリクス再入門 - プログラマーの脳みそ
Java5から導入されたジェネリクスの詳しい解説。
Java総称型メモ(Hishidama's Java Generics Memo)
Java5から導入されたジェネリクスの例つきの解説。
Javaの理論と実践: Generics、了解!
Java5から導入されたジェネリクスの導入背景。
なぜ Java の配列は共変で、Generics は共変ではないのか - sinsengumi血風録
配列が共変でないのはJavaランタイムへの互換性のためのErasureの仕組みが根本原因のようです。
java - Generic data type conversion method - Stack Overflow
Java8では型推定機能が強化されているようです。
Java新機能メモ(Hishidama's Java up-to-date)
Javaの各バージョンの違いがわかりやすいです。
【祝】Java SE 8 正式リリース | 寺田 佳央 - Yoshio Terada
Java8の変更点がよくわかります。
6. 式 (expression) — Python 3.3.6 ドキュメント
Pythonのラムダ記法。
ラムダ式・Stream APIの理解のポイントは「型」 - Java EE 事始め!
Java8で導入されたJavaのラムダ記法。
ここが大変だよJava 8 Date-Time API(1):ImmutableでスレッドセーフになったJavaの新しい日時APIの基礎知識 (1/5) - @IT
Java8では日時APIも変更されました。
0 件のコメント:
コメントを投稿