SSブログ

可変長パラメータと配列パラメータの互換性 [Java]

メソッドがオーバーライドされる場合、スーパークラスでは引数型が配列と宣言されたメソッドを、サブクラスで可変長パラメータと宣言したり、スーパークラスで引数型を可変長パラメータと宣言されたメソッドを、サブクラスで配列と宣言したりすると、JDK 5.0リリースではコンパイルができますが、警告メッセージが出力されていました。

たとえば、次のAクラスとBクラスを見てみてください。
class A {
    void f(Object... o) { }
    void g(Object[] o) { }
}

class B extends A {
    void f(Object[] o) { }
    void g(Object... o) { }
}

JDK 5.0では、これらのクラスをコンパイルすると警告メッセージが出ていました。しかし、Java 6では、警告メッセージは何も出ることなくコンパイルされるようになっているようです。JDK5.0がリリースされた時点では、将来はコンパイルエラーされる可能性も考えられていましたが、Java 6では警告は何も出ないようになっているので、将来コンパイルエラーとされる可能性はほとんどないでしょう。

しかし、警告が出なくても、やはり、どちらのオーバーライドも好ましくないでしょう。
class Test {
  public static void main(String[] args) {
    B b = new B();
    A a = b;

    a.f(1, 2, 3);
    b.f(1, 2, 3);

    b.g(1, 2, 3);
    a.g(1, 2, 3);
  }
}

このTestクラスをコンパイルすると次のようにエラーとなります。
Test.java:10: g(java.lang.Object[]) (A 内) を (int,int,int) に適用できません
    a.g(1, 2, 3);
     ^
エラー 1 個

Bクラスのfメソッドは、可変長パラメータと同じように呼び出せています。gメソッドの場合には、参照の型によって、スーパークラスは配列パラメータ、サブクラスは可変長パラメータと見なされていることになります。

[追記: 2009年5月2日]
一部訂正ブログを書いていますので、そちらも参照してください。

アノテーションの互換性 [Java]

『プログラミング言語Java第4版』の第16章「リフレクション」の16.2節「アノテーションの問い合わせ」では、リフレクションを使用してのアノテーションの問い合わせ方法が解説されています。その節の最後の段落に次のような記述があります(363頁)。
実行時に存在するアノテーション型は、調べられようとしているクラスにアノテーションを付けるのに使用されたアノテーション型と異なる可能性がありますので、その型の2つの使われ方には互換性がない可能性があります。もし、互換性がない場合に、アノテーションの要素へアクセスしようとすると、AnnotationTypeMismatchExceptionあるいは IncompleteAnnotationExceptionがスローされます。もし、要素型がenumであり、アノテーション内のenum定数がそのenumに存在しなくなっていれば、EnumConstantNotPresentExceptionがスローされます。

この説明によれば、アノテーションは、互換性がある方法で変更することが可能だということになります。互換性を保ちながらアノテーションを変更できるという意味で、『Effective Java 第2版』には、次のように述べられています(174頁の最後の段落)。
マーカーインタフェースに優るマーカーアノテーションの主な長所は、デフォルトを持つアノテーション型要素を1つ以上追加することで、すでに使用された後でもアノテーション型に情報を追加できることです [JLS, 9.6]。単なるマーカーアノテーション型として始まっても、時間の経過に伴い機能が豊富なアノテーション型へ発展できます。そのような発展は、マーカーインタフェースでは不可能です。なぜなら、一旦実装された後に、インタフェースにメソッドを追加することは一般に不可能だからです。

つまり、既存のアノテーション型の定義に新たな要素を追加する場合には、デフォルトを指定すれば互換性を保ちながら変更が可能だということです。

実際に、既存のアノテーション型にデフォルトを持たない要素を追加した場合には、そのアノテーション型でアノテーションを付けられているコードを再コンパイルするとエラーになります。なぜならば、新たな要素の値が指定されていないからです。しかし、再コンパイルされない場合に、リフレクションによりその新たな要素の値を読み出そうとすると、IncompleteAnnotationExceptionがスローされます。

既存のアノテーション型の特定の要素の型を変更した場合には、そのアノテーション型でアノテーションを付けられているコードを再コンパイルするとエラーになったり、ならなかったりします。たとえば、元の要素の型がStringであったのををintにすればコンパイルエラーとなります。一方、元の要素の型がStringであったのを配列String[]に変更した場合には再コンパイルできます。なぜならば、要素の値が少なくとも1つはすでに指定してあるからです。しかし、どちらの場合でも、古いアノテーション型でアノテーションが付けられているコードを再コンパイルすることなく、そのアノテーションの要素の値をリフレクションにより読み出そうとするとAnnotationTypeMismatchExceptionがスローされます。

Java言語のアノテーションについてはきちんと学びたい方は、『プログラミング言語Java第4版』の第15章「アノテーション」と16.2節「アノテーションの問い合わせ」、および、拙著『Java 2 Standard Edition 5.0 Tiger―拡張された言語仕様について』の第7章「アノテーション」と8.1節「リフレクションAPI」を読まれることをお勧めします。


Top Java Developers Offer Advice to Students [Java]

Sun Developer Networkに「Top Java Developers Offer Advice to Students」と題する記事が掲載されています。これは、過去のインタビュー記事のダイジェスト版になっており、各インタビューの最後には元のインタビュー全文へのリンクがあります。

Top Java Developers Offer Advice to Students

Java言語のenum型 [Java]

『Effective Java 第2版』の出版後、いくつかのブログには『Effective Java 第2版』を読んで「enum型の様々な機能や使用方法を初めて知った」というものがあり、不思議に思って書店でいわゆる初心者本をチェックしてみました。

12月27日(土)にJava読書会へ参加したあと、青葉台のブックファーストで初心者向けのJavaの書籍をそこにあるだけチェックしたのですが、enum型に関しては、C/C++言語のenumと同じような機能部分だけをあっさりと説明している書籍ばかりでした。

『Effective Java プログラミング言語ガイド』で説明されていたタイプセーフenumから発展して導入されたenum型は、かなり強力なのですが、その強力な点はほとんど説明されていませんでした。

『プログラミング言語Java第4版』(2007年4月発売)では、第6章「列挙型」として9ページを費やして解説されていますし、拙著『Java 2 Standard Edition 5.0 Tiger―拡張された言語仕様について』(2005年5月発売)でも第4章「enum型」として15ページを費やして解説しています。

enum定数とシリアライズ [Java]

書籍『Effective Java 第2版』では、項目3の最後にシングルトンを実装するためには、「現在ではシングルトンを実装する最善の方法は単一要素のenum型も用いることです。」と述べられています。特に、シリアライズ可能なシングルトンを実装するのであれば、そうです。そのため、項目77「インスタンス制御に対しては、readResolveよりenum型を選ぶ」と独立した項目も書かれています。

項目77のp.300には次のようにも述べられています。
enumとしてシリアライズ可能なインスタンス制御されたクラスを書くのであれば、宣言された定数の他にインスタンスが存在しないという完全な保証を得ます。JVMがその保証を行いますし、みなさんはそれに頼ることができます。みなさんの方で特別なことは必要ありません。

シリアライズに関しては、enumは特別に扱われます。それに関しては、拙著『Java 2 Standard Edition 5.0 Tiger 拡張された言語仕様』の4.7節「enum定数のシリアライズ」で説明しています。

Java 2 Standard Edition 5.0 Tiger―拡張された言語仕様について

Java 2 Standard Edition 5.0 Tiger―拡張された言語仕様について

  • 作者: 柴田 芳樹
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2005/04
  • メディア: 単行本


4.7節は短いので、その全文は以下の通りです。
java.lang.Enumクラスは、Serializableインタフェースを実装していますが、readResolveメソッドは定義されていません。従来のタイプセーフenumでは、readResolveメソッドを実装しなければ、正しくディシリアライズできませんでした。

リリース5.0からは、シリアライズ/ディシリアライズの仕様が変更になっており、enum定数は特別に処理されるようになっています。enum定数をシリアライズする場合には、nameフィールドだけがシリアライズされます。他のフィールドは一切シリアライズされません。そして、ディシリアライズでは、nameフィールドの値を用いて、Enum.valueOfメソッドを使用して値オブジェクトを取得するようになります。

enum型の宣言で、シリアライズ/ディシリアライズのためのカスタマイズ用メソッド(writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve)を定義しても、すべて無視されます。同様に、serialPersistentFieldsserialVersionUIDもすべて無視され、serialVersionUIDは、常に0Lとなります。


拙著ではリリース1.5で導入された言語仕様に関する制限事項や、どのように実装されているかについて説明しています。ただし、私自身の用語に対する勘違いなどの誤りがありますので、参考にされる際には、正誤表も参照をお願いします。

『Java 2 Standard Edition 5.0 Tiger』正誤表
http://www001.upp.so-net.ne.jp/yshibata/tigererrata.html

第8期「プログラミング言語Java」教育終了 [Java]

以前、第7期「プログラミング言語Java」教育の終了を報告しましたが、12月19日(金)に第8期が終了しました。今年度から開始時期をずらして、2コース開催しています。したがって、現在第9期が行われており、来月より第10期が開始されます。

最終回は成果発表会で主に一年間を振り返って各人に発表してもらいます。多くの受講生が述べていましたが、もし独学で書籍『プログラミング言語Java第4版』と『Effective Java第2版』を読もうとしていたら、途中で挫折していただろうと。

Java言語によるソフトウェア開発に従事しているソフトウェアエンジニアには、書籍『プログラミング言語Java第4版』と『Effective Java第2版』の両方を最低でも学習してもらいたいところです。しかし、初心者には難しいため、実際に両方を全部読んで理解している人は、非常に少ないのではないかと思います。

その意味で、現在はグループ関連会社に限定していますが、多くのソフトウェアエンジニアを育成する意味で、来年度はグループ関連会社外に対しても、機会があれば実施してみたいと思っています。

Bug Id: 6783209 [Java]


プログラミング言語Java (The Java Series)

プログラミング言語Java (The Java Series)

  • 作者: ケン・アーノルド
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/04
  • メディア: 単行本


『プログラミング言語Java第4版』の551頁にFormatterクラスの説明で%%に関して、以下のような記述があります。

2 つの特別な変換を手短に説明します。最初の変換は、%を出力するのに使用される%変換です。%文字 はフォーマット指定子の開始を示しますので、出力に実際に%文字が含まれるようにする方法が必要です。 フォーマット指定子%% は、それを行います(単一文字\を生成するのに、エスケープ文字\\が使用されるのと同じです)。出力に空白を埋めるためにこの変換と一緒に幅を指定できます。フラグが指定されなければ左に 空白を埋め、-フラグが左寄せのために指定されていたら右に空白を埋めます。

これは、もともとFormatterクラスの仕様を元に記述されているのですが、実は、これは正しく動作しません。社内で行っている「プログラミング言語Java」教育で、この部分のテストコードを書いたが期待通りに動作しないと質問があり、私自身も簡単なテストコードを書いて確認しました。やはり、上手く動作しないので、先日不具合であると報告したので、Bug Databaseに登録されました。

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6783209

リリース1.5の初めからバグだった訳ですから、長い間、誰も報告しなかったようです。

Java読書会へ明日は参加します [Java]

最後に参加したのが2006年1月なので、約3年振りにJava読書会に参加します。

http://www.javareading.com/bof/



『Effective Java 第2版』届きました [Java]

ej2e.jpg

土曜日の午前中に、出版社から『Effective Java 第2版』が(翻訳契約に含まれる)5冊届きました。Amazonの発売予定日は、11月27日になっていますが、おそらく次の三連休中には書店によっては並ぶのではないかと思います。

英語版が今年5月のJavaOneで先行発売されてから、約半年後に日本語版を出すことができました。初版と同様に、翻訳版に関しては日本語版が最初に出版されたのではないかと思っています(出版社に確認していませんが・・・)。

英語版での第3刷(third printing)までに見つかった誤りは、日本語版では修正されています。また、本文を補足するための「訳注」も10個ほど新規に追加しています。しかし、やはり、初心者には内容的に難しいかもしれません。時間があれば、「Effective Java 第2版を読む」と題したブログを書いていきたいと考えています(本当に書けるかは約束はできませんが・・・)。

初版を手にとって読み始めた時にはJoshua Blochの名前も知りませんでした。しかし、初版の翻訳を通して、電子メールのやり取りをして、2002年の12月に初めて会って(拙著『プログラマー現役続行』のp.48の写真)以来、何度か会って話をしています。

彼の著書である『Effective Java』の初版、第2版、それに、『Java Puzzlers 罠、落とし穴、コーナーケース』を翻訳することで、私自身は多くの事柄を学びました。第2版を日本の多くのソフトウェアエンジニアが読まれて、日本のソフトウェア業界のレベルアップに多少なりとも結びつけばと思っています。

『Effective Java 第2版』 Amazon.co.jpで予約可能 [Java]


Effective Java 第2版

Effective Java 第2版

  • 作者: Joshua Bloch
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2008/11/27
  • メディア: 単行本(ソフトカバー)


Amazon.co.jpで予約注文できるようになりました。また、出版社のホームページにも紹介されています。

出版社紹介ページ