Javaで,匿名クラス内で定義したpublicメソッドの警告が消せず困った話 (静的なJavaと,動的なJavaScriptを連携させるDSLを作りたい)
重要なお知らせ:
この記事で公開した情報は,AndroidのMVCフレームワーク「Android-MVC」の機能の一部として取り込まれました。
より正確な設計情報や,動作可能な全ソースコードを閲覧したい場合,「Android-MVC」の公式ページより技術情報を参照してください。
AndroidのMVCフレームワーク - 「Android-MVC」
http://code.google.com/p/android-mvc-...
ある目的のために,Javaで,無名クラス内にpublicメソッドをたくさん定義した。
全部,Overrideではないメソッドである。
なので,Eclipseに「このメソッドは使用されません」の警告が大量に表示されてしまった。
警告を消すためには,1つメソッドを定義するごとに毎回,「@SuppressWarnings("unused")」のアノテーションを付与しなければならない。
この手間は,非常に煩雑。コードの量も増えてしまう。スマートでない。
何とか解消できないか?と悩んだ。
この件に関して,Stack Overflow に質問を(英語で)投稿した。
私が悩んだ思考過程のログとして,質問の翻訳文を掲載する。
How to avoid @SuppressWarnings with anonymous inner classes in Java?
http://stackoverflow.com/questions/11...
翻訳は,技術的に正確でありつつも,雰囲気が伝わりやすいよう,口語的に意訳しておく。
〜〜内容の全訳 ここから〜〜
Javaの無名インナークラスで,@SuppressWarnings を使わないで済ませたい。
匿名クラスを使う時に,スマートなコーディングスタイルができなくて,めっちゃ困ってます。
下記のケースなんですが,匿名のインナークラスを使う時に,どうすれば @SuppressWarnings しないで済みますか?
匿名クラスをこんな感じで使う場合なんですけど:
someMethod( new Y(){ // overrideしていないpublicなメソッド public void echo1(){ // 何かの処理 } } ); ... public abstract class Y { // このクラスは匿名クラスとして利用する。 // クラス定義時にメソッドも内部に定義すること。 }
このコードだとwarningが出ちゃうんです。
「echo1」というpublicメソッドは,@SuppressWarnings("unused") のアノテーションをつける必要があります。
もし,echo2, echo3, echo4,・・・といった具合にコードを追加していく場合,全部のメソッドに対して一つずつ,アノテーションを付与しないといけません。
アノテーションばっかり書くという事態を回避したいので,ちょっと試してみたんですが,
「継承する子クラス全体に対してアノテーションの効果を伝搬させる」
ような事をできないかな?と思って,
下記のようにコーディングしてみました。
@Inherited // ←このアノテーションは,こうであって欲しいんですよ! @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface SuppressWarningsPropagate extends SuppressWarnings { }
それで,この新しい独自アノテーションを使って,クラスYだけにアノテーションを付与すればいいや,と考えました。
そうすれば,たった一つの基底クラスにアノテーションをつけるだけで済むので,便利でシンプルになるはずだったんです。
・・・で,私はバカだった,と気づきましたよ。
SuppressWarningsとかのアノテーションって,extendsもimplementsもできないんですよね。
もちろん,下記のコードなら動くし,warninigも無いんですけど。
具象クラス1個に対してSuppressWarningsする分にはうまくいくって事です。
@SuppressWarnings("unused") public class X { private int i; private int j; }
ここまでの質問をまとめます。どうしたら・・・
- (コードの可読性のために)匿名インナークラスを使い,
- なおかつ,new時にpublicメソッドを定義し,(※publicじゃないとダメです。Android SDK の addJavascriptInterface() の都合なんですけど)
- なおかつ,(コードの書きやすさのために)全メソッドに対して一つずつ @SuppressWarnings("unused") を書かなくて済むようにする。
・・・というのを実現できるんでしょうか? こんな事考えるのって,自分は夢見すぎですか?
ご協力に前もって感謝いたします。
タグ:java, android
lang.and.engineより。
コメント欄でのやり取り
Flavio氏より:
その警告が意味するところは,あなたが作った echo1() を誰も使ってないよって事です。だから,そのメソッドを削除しても,実行結果は同じになるはずです。何をやりたいんですか?
私から返信:
Flavioさん,こんにちは。コメントありがとうございます。
全くもって仰る通りだと思います。
でも,補足の説明があるんです・・・。
是非お聞きいただけるでしょうか。きっと興味深いと思いますので。詳細を,3つの部分に分けて投稿しますね。
- (1)目的,
- (2)問題,
- (3)質問。
1個ずつのコメントは短くないと投稿できないみたいですので。
(1)目的。
私がやろうとしている事は,
静的言語であるJavaと,動的言語であるJavaScriptを連携させる,という事です。
これは,Androidアプリを開発するときなんかに皆よくやってます。
HTMLとJavaScriptから,Javaのメソッドを呼べるんです。
参考サイト:
"call java function from javascript over android webview":
http://stackoverflow.com/questions/10...私が書いた説明(自動翻訳でごめんなさいね)
http://translate.google.co.jp/transla...
でも,そういうJSから呼び出し可能なJavaメソッドは,全部publicである必要があります。
これは単にAndroid APIの仕様なんです。そのために,私みたいな状況が生じるんです。
(2)問題。
Javaとか,Eclipseとか,静的に型付けされたプログラムの世界の中では,
publicなJavaメソッド「echo1」がどこかでコールされるなんて事は,絶対に知りえないわけです。
実際,それらのメソッドは,Javaの世界の中では全く呼ばれません。
そうじゃなくてJavaScriptの世界で呼ばれる目的でコーディングしたわけですから,Eclipseはその事を検知しようがないわけです。
なので,Eclipseの警告は別にいいんです。
「このメソッド,Javaの世界では使われてないぞ!」って,ご親切に教えてくれてるんですよね。
それ自体は構わないんですが・・・。
(3)質問。
それで,私の質問が発生するんです。
こういう状況下で,Eclipseの警告メッセージを,簡単に消去もしくは抑止できるんでしょうか?
JS側で呼ぶためのメソッドを1個追加したいと思うたびに,@SuppressWarnings("unused") のアノテーションを書かなきゃいけないんです。
もしSuppressWarningsアノテーションを「拡張」して,子クラスに性質が継承されるようにできたら,書く回数が一回だけで済むんですけど。
でもJavaの標準アノテーションって,拡張する事はできませんし,SuppressWarningsアノテーションを子クラスに継承されるような仕方で使う事もできないです。
そんなわけで,この質問をさせて頂きました・・・。
もちろん,この質問が変に聞こえるだろうな,というのは承知しています。
このトピックは,Javaの標準的なセオリーだけでなく,静的でないJSのスクリプティングにもまたがった話になってますからね。
もし不思議に思わせて混乱させてしまったら,ごめんなさいね。
では,どんなアイデアでも結構ですので,お待ちしております。
私のコメントは以上です。
Flavio氏から返信:
多分,できる事といったら,その手のwarningをプロジェクト全体でオフにするようにEclipse側で設定するぐらいなんじゃないですか?
下記を参照してください。
http://stackoverflow.com/questions/10...
私から返信:
Flavioさん,上記でお伝えした状況はいい感じにカオスってるにも関わらず,返信して下さりありがとうございます。
教えて下さったとおりにする事は可能でした。
(ウィンドウ>設定>Java>コンパイラ>エラーと警告>未使用のローカルまたはprivateメンバ>無視)
この操作のおかげで,この手の警告を全部消す事は,確かに可能だったのですが・・・
でも残念ながら,いろんな事情があるため,この方法を採用するのは厳しいです。
私は,みんなが使えるようなライブラリを作ってます。
なので,私が提供できるのはコードだけで,ユーザに対してIDEの設定を変更するように強制する事はできないんです。
こういう設定項目を,コードだけを使って実現して解決できたらよかったんですけどね。(例えば,クラスに対してアノテーションを付与するとか。)
とにかく,助けになる情報をご提供下さり感謝いたします。:-)
〜〜翻訳はここまで。〜〜
補足説明
冒頭で述べたシチュエーションが発生する原理が,お分かりいただけただろうか。
JavaScriptとJavaを橋渡ししようとして,しかもそのコードをDSLとして簡潔に実装可能にするためには,匿名クラスによる記法をどうしても採用したい。
だが,JavaScript側で使いたくなるであろうメソッドの名前なんて,事前にJava側では知りえないので,匿名クラスの基底クラスでメソッドにabstract宣言しておくことはできない。
むしろ,匿名クラス宣言時に,Overrideではない仕方で,自由にメソッドを定義する事になる。
そこでEclipseが「余計な」事を言い出すのだ。
- 「このクラス,せっかく例外的な仕方で宣言して,わざわざメソッドを付け足したにも関わらず,Java内ではどこでも呼ばれてないぞ。public宣言したからには,よそで使う事を意図してるんだろう?」
と。
私の返事はこうである:
- 「そりゃそうだよ。でも,Javaじゃなくて,JavaScriptで呼ばれるんだよ。まあ残念ながら静的な君には理解不能な事だから,君には余計な手間をかけさせて申し訳ないんだけどさ…。警告を出さなくていいから。」
Eclipseの返事はこうである:
- 「そうはいくか。静的な世界で,全ての参照を管理するのが私のミッションだ。(以下論争)」
・・・。
解決策
それで結局,Eclipseと私の間は平行線をたどった。
そのため,私は自分のプロジェクトの納期を吟味して,何らかの対策を打たねばならなかった。
解決策はこうである。
匿名クラスをやめ,インナークラスにする。
同一クラス内で:
someMethod( new concreteY() ); ・・・ public class concreteY extends Y { // overrideしていないpublicなメソッド public void echo1(){ // 何かの処理 } }
こうする事で,一切の警告は消える。
匿名クラスではなくなったからだ。
しかし,その代償として,コードの可読性は落ちた。
HTML側にバインドできるJavaScriptのAPIの一覧を,メソッドチェインの中に,自然に組み込みたかった。
このシチュエーションでは,匿名クラスは最適のソリューションだった。
なぜなら,その画面でしか使われない,本当に使い捨てのクラスだからだ。
その画面だけで使うHTMLに対して,その画面特有のJavaScript APIを記述することが目的だった。
だから,わざわざ別個にクラス名なんぞを付与する必然性は本来なかったのだ。
(※なお,このへんの感覚・発想法は,クラス数とかコードの行数を増やす事に喜びを感じるような,古いJava使いには通じない。静的言語と動的言語の,両者の長所と短所をすべてバランスよく把握している開発者には通じる。)
まあ,しょうがない。
- 「仕様書のようなコード」「仕様書としてのコード」
を実現するにあたって,
JavaScriptのインタフェースだけは「別紙参照」状態になる。
こう考えて割り切ることにする。
そして,他の面で少しでも負荷を減らし,AndroidとHTML(+HTML5+jQuery Mobile)の連携を容易にしてゆく。
それがミッションである。
参考:
たった2ファイルで,HTML+JS製のネイティブAndroidアプリを作る手順 (動作するサンプルコード付き。WebViewの活用方法)
http://language-and-engineering.hatenablog.jp/entry/20120710/CreateAndroidApp...
AndroidやiOSの「ハイブリッドアプリ」で,JavaScriptとネイティブ・コードが連携する仕組みを図解 (おまけ:HTML側で施すべき,クロスプラットフォーム対策)
http://language-and-engineering.hatenablog.jp/entry/20120713/p1
jQuery Mobile と HTML5 で、Androidのネイティブアプリを作成する手順
http://language-and-engineering.hatenablog.jp/entry/20120717/CreateAndroidNat...
Javaの独自アノテーション定義方法について:
制御しやすい「デバッグ用ロガー」を自作して,サクサク開発 (Javaで,メソッド名を含めログ出力する方法のサンプル)
http://language-and-engineering.hatenablog.jp/entry/20120209/p1