2011年7月23日土曜日

JavaSE7のtryは便利

7月28日に正式リリースされるJavaSE7では、いくつか便利な構文が増えます。
今回は、その中でも特に有用であろうtryの新しい構文について調べてみました。

ファイルのコピーを例にして、tryの構文の拡張を調べてみます
JavaSE6以前のソースコードをJavaSE7のコードに置き換えます。

これまで(JavaSE6以前)のコード
  1. FileInputStream fis = null;  
  2. FileOutputStream fos = null;  
  3. try {  
  4.  fis = new FileInputStream("D:\\test.txt");  
  5.  fos = new FileOutputStream("D:\\test2.txt");  
  6.  byte[] bytes = new byte[1024];  
  7.  int size = -1;  
  8.  while ((size = fis.read(bytes)) != -1) {  
  9.   fos.write(bytes, 0, size);  
  10.  }  
  11. catch (FileNotFoundException e) {  
  12.  e.printStackTrace();  
  13. catch (IOException e) {   
  14.  e.printStackTrace();  
  15. finally {  
  16.  if (fos != null) {  
  17.   try {  
  18.    fos.close();  
  19.   } catch (IOException ex) {  
  20.   }  
  21.  }  
  22.  if (fis != null) {  
  23.   try {  
  24.    fis.close();  
  25.   } catch (IOException ex) {  
  26.   }  
  27.  }  
  28. }  

これから、上記のソースコードをJavaSE7風に書き換えていきます。

例外のマルチキャッチ
catchの部分をひとつにまとめることができます
  1. FileInputStream fis = null;  
  2. FileOutputStream fos = null;  
  3. try {  
  4.  fis = new FileInputStream("D:\\test.txt");  
  5.  fos = new FileOutputStream("D:\\test2.txt");  
  6.  byte[] bytes = new byte[1024];  
  7.  int size = -1;  
  8.  while ((size = fis.read(bytes)) != -1) {  
  9.   fos.write(bytes, 0, size);  
  10.  }  
  11. catch (FileNotFoundException | IOException e) {  
  12.  e.printStackTrace();  
  13. finally {  
  14.  if (fos != null) {  
  15.   try {  
  16.    fos.close();  
  17.   } catch (IOException ex) {  
  18.   }  
  19.  }  
  20.  if (fis != null) {  
  21.   try {  
  22.    fis.close();  
  23.   } catch (IOException ex) {  
  24.   }  
  25.  }  
  26. }  
これで、例外処理時に同じコードを書かずに済みます。
また、「|」で複数の例外を指定した場合は、左の例外から評価されるようです。
これまでの構文のcatchで上に書いていた例外は、その順で左から書けばよいでしょう。つまり、Throwableを書く場合は、一番右です。
AutoCloseableインターフェース
JavaSE7から、AutoCloseableというインターフェースが追加されます。
このインターフェースには、closeというメソッドがあります(ご存知のとおり、JavaSE6以前にもcloseメソッドはありますが、それをオーバーライドしている感じです)。このメソッドが「try-finallyの処理で勝手に呼ばれる」ようです。 AutoCloseableを利用するためのコードは下記の通りです。
  1. try (FileInputStream fis = new FileInputStream("D:\\test.txt"); FileOutputStream fos = new FileOutputStream("D:\\test2.txt")) {  
  2.  byte[] bytes = new byte[1024];  
  3.  int size = -1;  
  4.  while ((size = fis.read(bytes)) != -1) {  
  5.   fos.write(bytes, 0, size);  
  6.  }  
  7. catch (FileNotFoundException | IOException e) {  
  8.  e.printStackTrace();  
  9. }  
tryの()の中ではセミコロンで区切ると、複数のAutoCloseableを並べて記述できるようです。
Stream関係のインターフェースはAutoCloseableのサブインターフェースになっているようです。
インターフェースを実装する際に、AutoCloseableを意識する必要はあまりないと思われます。ъ(゚Д゚)グッジョブ!!(JavaSE6以前のソースを書き換える場合は別)
※public void close() throws IOException => public void close() throws Exception というふうに変わります。

なにはともあれ、コード量がかなり減りました。ワーイヽ(゚∀゚)メ(゚∀゚)メ(゚∀゚)ノワーイ
AutoCloseableを検証
実際に自分の眼で見て確認しました。
closeの順番と、実際に例外が起こった場合について。
  1. public class Test {  
  2.  public static void main(String[] args) {  
  3.   new Test().start();  
  4.  }  
  5.   
  6.  void start() {  
  7.   try (AutoCloseableImpl1 ac1 = new AutoCloseableImpl1(); AutoCloseableImpl2 ac2 = new AutoCloseableImpl2()) {  
  8.      
  9.   }  
  10.  }  
  11. }  
  12.   
  13. class AutoCloseableImpl1 implements AutoCloseable {  
  14.   
  15.  AutoCloseableImpl1() {  
  16.   System.out.println(getClass().getSimpleName());  
  17.  }  
  18.   
  19.  @Override  
  20.  public void close() {  
  21.   System.out.println(getClass().getSimpleName() + "#close()");  
  22.  }  
  23. }  
  24.   
  25. class AutoCloseableImpl2 implements AutoCloseable {  
  26.   
  27.  AutoCloseableImpl2() {  
  28.   System.out.println(getClass().getSimpleName());  
  29.  }  
  30.   
  31.  @Override  
  32.  public void close() {  
  33.   System.out.println(getClass().getSimpleName() + "#close()");  
  34.  }  
  35. }  
上記のプログラムを実行します。
実行結果は

AutoCloseableImpl1
AutoCloseableImpl2
AutoCloseableImpl2#close()
AutoCloseableImpl1#close()

closeが呼ばれており、closeする順番もオッケー。
ちなみに、AutoCloseableImpl2()コンストラクタで例外を投げた場合は、以下

AutoCloseableImpl1
AutoCloseableImpl1#close()
Exception in thread "main" java.lang.NullPointerException
・・・

期待通りです。

余談ですが、catchもfinallyもないtry構文が書けるようになりました。(内部的にはfinallyが存在しますが)

実は、JavaSE7にはjava.nio.file.Filesというクラスが追加されており、ファイルをコピーするだけなら、このクラスを使ったほうが良いです。
まぁ、今回はあくまで勉強のためですから・・・

今回の検証でもちいたIDEはNetBeansです。
Eclipse3.7でも検証は可能でしたが、不都合な点がありましたので、NetBeansを使用しました。
現在のEclipse3.7とJDK1.7の詳細については こちら

2011年7月17日日曜日

Eclipse3.7 で JavaSE7

Eclipse3.7 で JavaSE7

JavaSE7の正式リリースは2011年7月28日で、NetBeansは既にJavaSE7に対応しています。
EclipseはまだJavaSE7に対応していないのですが、pluginをインストールすれば使えるようになります。

各ツールのサイト
Java™ Platform, Standard Edition 7 Developer Preview Release
Eclipse Classic 3.7
JDT/Eclipse Java 7 Support(BETA)
JDK準備
JDK7 Preview Releaseをダウンロードして、インストールします。
開発ツールだけインストールすればOKです。
Eclipse準備
Eclipseは3.7の「Classic」を使います。
「IDE for Java Developers」や、「IDE for Java EE Developers」では、pluginのインストールができませんでした。
Install New SoftWare -> Work with:
「http://build.eclipse.org/eclipse/java7patch/」を入力して、pluginをインストールします。
Eclipse設定
Preferences -> Java -> Installed JREs
インストールしたJDK1.7を追加し、使用するようにします。
あとは、Java Compilerの設定で、Compiler compliance level を 1.7 にすれば準備完了です。

実は、上記で設定した開発環境には問題があり、メソッドや変数の候補を出せなくなります。
普段から、何も考えずにとりあえず Ctrl + Space 押して幸せになっている私にとっては致命的です。
Eclipseで正式にサポートされるまではJavaSE7は我慢するか、NetBeansを使いましょう。