Se ha denunciado esta presentación.

Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜

9

Compartir

1 de 35
1 de 35

Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜

9

Compartir

Descargar para leer sin conexión

Descripción

JJUG CCC 2017 Spring L7セッションの発表資料です。

Transcripción

  1. 1. Javaチョットデキルへの道 ~JavaコアSDKに見る真似したいコード10選~ JJUG CCC 2017 Spring 2017/05/20 #jjug_ccc #ccc_l7 Made with エルナナ
  2. 2. 自己紹介 • 株式会社ジャストシステム 福嶋 航 • Twitter @fukushiw • Java歴約20年、JavaでWebサービス作っています • #Java100 本ノックの人 https://github.com/JustSystems/java-100practices
  3. 3. 今日お話しすること 無料でJava力アップ! JavaコアAPIのソースはまさに宝の山。 Java 100本ノック作者がその中から選りすぐりの 10コードブロックについて、どう優れているのかこれか ら書くコードに是非採用したくなるような、匠の技を 紹介します。
  4. 4. はじめに
  5. 5. JavaコアAPIとは • JDKをインストールするとついてくる src.zip • Java SE Development Kit 8u131 コメント+空行が約53.6% 7,650 1,087,643 1,004,582 251,013 2,343,238 Files Lines of Code Effective Lines of Code Comment Lines of Code Blank Lines of Code Total
  6. 6. 本題
  7. 7. 初級編 1~4
  8. 8. 1 NullPointer Exception ダメ。ゼッタイ。
  9. 9. NullPointerException ダメ。ゼッタイ。 外からやってくるものは必ず null チェック! 例:java.util.Arrays LL.3998-4007 public static int hashCode(char a[]) { if (a == null) return 0; int result = 1; for (char element : a) result = 31 * result + element; return result; }
  10. 10. NullPointerException ダメ。ゼッタイ。 特にインスタンスを生成するときは上記のように明示的にnullチェックをしておく。 インスタンスは生成できたが、使うときに(インスタンス生成時のnull渡しが原因で) NullPointerException が発生、となると原因が追いにくくなる。 例:java.time.LocalDateTime LL.373-377 public static LocalDateTime of(LocalDate date, LocalTime time) { Objects.requireNonNull(date, "date"); Objects.requireNonNull(time, "time"); return new LocalDateTime(date, time); }
  11. 11. 2 ガード節
  12. 12. ガード節 例:java.lang.Thread LL.325-341 public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } 典型的なガード節と例外スロー。 その後の処理ロジックで異常な状態を考慮しなくてよいようにす ることで可読性を向上している。 例外スロー時にちゃんとメッセージを設定しているところも注目。
  13. 13. 3 例外メッセージ は丁寧に
  14. 14. 例外メッセージは丁寧に 何がいけないのかをちゃんと説明する 例:java.net.HttpURLConnection LL.237-251 * @throws IllegalStateException if URLConnection is already connected * or if a different streaming mode is already enabled. * * @see #setFixedLengthStreamingMode(int) * @since 1.5 */ public void setChunkedStreamingMode (int chunklen) { if (connected) { throw new IllegalStateException ("Can't set streaming mode: already connected"); } if (fixedContentLength != -1 || fixedContentLengthLong != -1) { throw new IllegalStateException ("Fixed length streaming mode set"); } chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen; }
  15. 15. 4 メソッドは短く シンプルに
  16. 16. メソッドは短くシンプルに 例:java.util.ArrayList LL.463-480 /** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } • コメント半分 • 直接関係ないロジックは サブルーチン化 1 2 3 4 5
  17. 17. 中級編 5~8
  18. 18. 5 パフォーマンス コンシャス
  19. 19. パフォーマンスコンシャス 例:java.lang.String LL.2066-2091 public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; } 1. oldChar != newChar で変え る必要性を確認 2. ループの中でインスタンス変数にア クセスしない 3. わざわざ oldChar の出現箇所を 探している ※もし出現しない場合は i == len となるので new char[len] も new String() もしない。 4. 3のために途中までしたループを無 駄にしない • 極力newしない • 極力インスタンス変数にアクセスしない
  20. 20. パフォーマンスコンシャス(拡大1) public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ 1. oldChar != newChar で変える必要性を確認 2. ループの中でインスタンス変数にアクセスしない
  21. 21. パフォーマンスコンシャス(拡大2) while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } 3. わざわざ oldChar の出 現箇所を探している ※もし出現しない場合は i == len となるので new char[len] も new String() もしない。 4. 3のために途中までした ループを無駄にしない • 極力newしない • 極力インスタンス変数にアクセスしない
  22. 22. 6 CloneNot Supported Exception の処理
  23. 23. CloneNotSupportedExceptionの処理 CloneNotSupportedException は実装次第では絶対に起きない。 (そもそもなぜ checked exception なのか不明) ここではその例外をキャッチ、起きえないことをコメントで示し、 InternalError をスローすることで対処。 → assert ができる前の苦肉の策と思われる。他に同様のクラスあり。 例:java.util.Vector LL.672-681 try { @SuppressWarnings("unchecked") Vector<E> v = (Vector<E>) super.clone(); v.elementData = Arrays.copyOf(elementData, elementCount); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); }
  24. 24. ちなみに http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4220218 JDK-4220218 : Please make CloneNotSupportedException unchecked Resolution : Won't Fix
  25. 25. 7 デザインパター ンの適用
  26. 26. デザインパターンの適用 ↑Factory Methodパターン その他GoF23パターンがどのコアAPIで使われているか、↓このまとめが秀逸 http://stackoverflow.com/questions/1673841/examples-of-gof- design-patterns-in-javas-core-libraries#answer-2707195 例:java.util.Calendar LL.313, 1611-1614 ※説明の便宜上折り返しを追加 public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> { public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); }
  27. 27. 8 サブクラスで スーパークラスに 定義された型を 狭める
  28. 28. サブクラスでスーパークラスに定義された 型を狭める UncheckedIOExceptionでは cause に IOException しか取らないように制 限している。 Serializable なので、デシリアライズで変なものが注入されないよう に、readObject()メソッドでちゃんとガードしている。 同様のガード例として、 java.time.chrono.JapaneseChronology#readObject()メソッドは常に InvalidObjectException をスローするようになっている、など。 例:java.io.UncheckedIOException LL.82-89 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); Throwable cause = super.getCause(); if (!(cause instanceof IOException)) throw new InvalidObjectException("Cause must be an IOException"); }
  29. 29. 上級編 9~10
  30. 30. 9 立つ鳥跡を 濁さず
  31. 31. 立つ鳥跡を濁さず 例:java.util.Timer LL.103-117 /** * This object causes the timer's task execution thread to exit * gracefully when there are no live references to the Timer object and no * tasks in the timer queue. It is used in preference to a finalizer on * Timer as such a finalizer would be susceptible to a subclass's * finalizer forgetting to call it. */ private final Object threadReaper = new Object() { protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.notify(); // In case queue is empty. } } }; コアAPIの中でも数少ない finalize() のオーバーライド。この threadReaperはどこからも参照されていないので、このfinalize()が 呼ばれるのはこのTimerインスタンスがお掃除されるとき。このときにちゃ んと(中で起動している)スレッドを終了させるための仕掛け。
  32. 32. 10 final変数への 代入
  33. 33. final変数への代入 ※黒魔術 例:java.math.BigInteger LL.4395-4465 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { : // Commit final fields via Unsafe UnsafeHolder.putSign(this, sign); : } // Support for resetting final fields while deserializing private static class UnsafeHolder { private static final sun.misc.Unsafe unsafe; : static void putSign(BigInteger bi, int sign) { unsafe.putIntVolatile(bi, signumOffset, sign); } : } よい子は真似してはいけません!
  34. 34. 最後に
  35. 35. お願い いいものを見つけたら、 Twitter: @fukushiw まで何卒ご一報下さい!

Descripción

JJUG CCC 2017 Spring L7セッションの発表資料です。

Transcripción

  1. 1. Javaチョットデキルへの道 ~JavaコアSDKに見る真似したいコード10選~ JJUG CCC 2017 Spring 2017/05/20 #jjug_ccc #ccc_l7 Made with エルナナ
  2. 2. 自己紹介 • 株式会社ジャストシステム 福嶋 航 • Twitter @fukushiw • Java歴約20年、JavaでWebサービス作っています • #Java100 本ノックの人 https://github.com/JustSystems/java-100practices
  3. 3. 今日お話しすること 無料でJava力アップ! JavaコアAPIのソースはまさに宝の山。 Java 100本ノック作者がその中から選りすぐりの 10コードブロックについて、どう優れているのかこれか ら書くコードに是非採用したくなるような、匠の技を 紹介します。
  4. 4. はじめに
  5. 5. JavaコアAPIとは • JDKをインストールするとついてくる src.zip • Java SE Development Kit 8u131 コメント+空行が約53.6% 7,650 1,087,643 1,004,582 251,013 2,343,238 Files Lines of Code Effective Lines of Code Comment Lines of Code Blank Lines of Code Total
  6. 6. 本題
  7. 7. 初級編 1~4
  8. 8. 1 NullPointer Exception ダメ。ゼッタイ。
  9. 9. NullPointerException ダメ。ゼッタイ。 外からやってくるものは必ず null チェック! 例:java.util.Arrays LL.3998-4007 public static int hashCode(char a[]) { if (a == null) return 0; int result = 1; for (char element : a) result = 31 * result + element; return result; }
  10. 10. NullPointerException ダメ。ゼッタイ。 特にインスタンスを生成するときは上記のように明示的にnullチェックをしておく。 インスタンスは生成できたが、使うときに(インスタンス生成時のnull渡しが原因で) NullPointerException が発生、となると原因が追いにくくなる。 例:java.time.LocalDateTime LL.373-377 public static LocalDateTime of(LocalDate date, LocalTime time) { Objects.requireNonNull(date, "date"); Objects.requireNonNull(time, "time"); return new LocalDateTime(date, time); }
  11. 11. 2 ガード節
  12. 12. ガード節 例:java.lang.Thread LL.325-341 public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } 典型的なガード節と例外スロー。 その後の処理ロジックで異常な状態を考慮しなくてよいようにす ることで可読性を向上している。 例外スロー時にちゃんとメッセージを設定しているところも注目。
  13. 13. 3 例外メッセージ は丁寧に
  14. 14. 例外メッセージは丁寧に 何がいけないのかをちゃんと説明する 例:java.net.HttpURLConnection LL.237-251 * @throws IllegalStateException if URLConnection is already connected * or if a different streaming mode is already enabled. * * @see #setFixedLengthStreamingMode(int) * @since 1.5 */ public void setChunkedStreamingMode (int chunklen) { if (connected) { throw new IllegalStateException ("Can't set streaming mode: already connected"); } if (fixedContentLength != -1 || fixedContentLengthLong != -1) { throw new IllegalStateException ("Fixed length streaming mode set"); } chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen; }
  15. 15. 4 メソッドは短く シンプルに
  16. 16. メソッドは短くシンプルに 例:java.util.ArrayList LL.463-480 /** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } • コメント半分 • 直接関係ないロジックは サブルーチン化 1 2 3 4 5
  17. 17. 中級編 5~8
  18. 18. 5 パフォーマンス コンシャス
  19. 19. パフォーマンスコンシャス 例:java.lang.String LL.2066-2091 public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; } 1. oldChar != newChar で変え る必要性を確認 2. ループの中でインスタンス変数にア クセスしない 3. わざわざ oldChar の出現箇所を 探している ※もし出現しない場合は i == len となるので new char[len] も new String() もしない。 4. 3のために途中までしたループを無 駄にしない • 極力newしない • 極力インスタンス変数にアクセスしない
  20. 20. パフォーマンスコンシャス(拡大1) public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ 1. oldChar != newChar で変える必要性を確認 2. ループの中でインスタンス変数にアクセスしない
  21. 21. パフォーマンスコンシャス(拡大2) while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } 3. わざわざ oldChar の出 現箇所を探している ※もし出現しない場合は i == len となるので new char[len] も new String() もしない。 4. 3のために途中までした ループを無駄にしない • 極力newしない • 極力インスタンス変数にアクセスしない
  22. 22. 6 CloneNot Supported Exception の処理
  23. 23. CloneNotSupportedExceptionの処理 CloneNotSupportedException は実装次第では絶対に起きない。 (そもそもなぜ checked exception なのか不明) ここではその例外をキャッチ、起きえないことをコメントで示し、 InternalError をスローすることで対処。 → assert ができる前の苦肉の策と思われる。他に同様のクラスあり。 例:java.util.Vector LL.672-681 try { @SuppressWarnings("unchecked") Vector<E> v = (Vector<E>) super.clone(); v.elementData = Arrays.copyOf(elementData, elementCount); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); }
  24. 24. ちなみに http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4220218 JDK-4220218 : Please make CloneNotSupportedException unchecked Resolution : Won't Fix
  25. 25. 7 デザインパター ンの適用
  26. 26. デザインパターンの適用 ↑Factory Methodパターン その他GoF23パターンがどのコアAPIで使われているか、↓このまとめが秀逸 http://stackoverflow.com/questions/1673841/examples-of-gof- design-patterns-in-javas-core-libraries#answer-2707195 例:java.util.Calendar LL.313, 1611-1614 ※説明の便宜上折り返しを追加 public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> { public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); }
  27. 27. 8 サブクラスで スーパークラスに 定義された型を 狭める
  28. 28. サブクラスでスーパークラスに定義された 型を狭める UncheckedIOExceptionでは cause に IOException しか取らないように制 限している。 Serializable なので、デシリアライズで変なものが注入されないよう に、readObject()メソッドでちゃんとガードしている。 同様のガード例として、 java.time.chrono.JapaneseChronology#readObject()メソッドは常に InvalidObjectException をスローするようになっている、など。 例:java.io.UncheckedIOException LL.82-89 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); Throwable cause = super.getCause(); if (!(cause instanceof IOException)) throw new InvalidObjectException("Cause must be an IOException"); }
  29. 29. 上級編 9~10
  30. 30. 9 立つ鳥跡を 濁さず
  31. 31. 立つ鳥跡を濁さず 例:java.util.Timer LL.103-117 /** * This object causes the timer's task execution thread to exit * gracefully when there are no live references to the Timer object and no * tasks in the timer queue. It is used in preference to a finalizer on * Timer as such a finalizer would be susceptible to a subclass's * finalizer forgetting to call it. */ private final Object threadReaper = new Object() { protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.notify(); // In case queue is empty. } } }; コアAPIの中でも数少ない finalize() のオーバーライド。この threadReaperはどこからも参照されていないので、このfinalize()が 呼ばれるのはこのTimerインスタンスがお掃除されるとき。このときにちゃ んと(中で起動している)スレッドを終了させるための仕掛け。
  32. 32. 10 final変数への 代入
  33. 33. final変数への代入 ※黒魔術 例:java.math.BigInteger LL.4395-4465 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { : // Commit final fields via Unsafe UnsafeHolder.putSign(this, sign); : } // Support for resetting final fields while deserializing private static class UnsafeHolder { private static final sun.misc.Unsafe unsafe; : static void putSign(BigInteger bi, int sign) { unsafe.putIntVolatile(bi, signumOffset, sign); } : } よい子は真似してはいけません!
  34. 34. 最後に
  35. 35. お願い いいものを見つけたら、 Twitter: @fukushiw まで何卒ご一報下さい!

Más Contenido Relacionado

Más de JustSystems Corporation

Libros relacionados

Gratis con una prueba de 30 días de Scribd

Ver todo

Audiolibros relacionados

Gratis con una prueba de 30 días de Scribd

Ver todo

×