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

35
Javaチョットデキルへの道 ~JavaコアSDKに見る真似したいコード10選~ JJUG CCC 2017 Spring 2017/05/20 #jjug_ccc #ccc_l7 Made with エルナナ

Upload: justsystems-corporation

Post on 21-Jan-2018

4.427 views

Category:

Engineering


0 download

TRANSCRIPT

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

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

JJUG CCC 2017 Spring2017/05/20#jjug_ccc

#ccc_l7Made with

エルナナ

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

自己紹介

• 株式会社ジャストシステム 福嶋 航• Twitter @fukushiw• Java歴約20年、JavaでWebサービス作っています• #Java100 本ノックの人https://github.com/JustSystems/java-100practices

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

今日お話しすること

無料でJava力アップ!

JavaコアAPIのソースはまさに宝の山。Java 100本ノック作者がその中から選りすぐりの

10コードブロックについて、どう優れているのかこれから書くコードに是非採用したくなるような、匠の技を

紹介します。

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

はじめに

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

JavaコアAPIとは

• JDKをインストールするとついてくる src.zip• Java SE Development Kit 8u131

コメント+空行が約53.6%

7,6501,087,6431,004,582

251,0132,343,238

FilesLines of Code EffectiveLines of Code CommentLines of Code BlankLines of Code Total

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

本題

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

初級編1~4

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

1

NullPointerException

ダメ。ゼッタイ。

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

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; }

Page 10: Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード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); }

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

2

ガード節

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

ガード節例: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); }

典型的なガード節と例外スロー。その後の処理ロジックで異常な状態を考慮しなくてよいようにす

ることで可読性を向上している。例外スロー時にちゃんとメッセージを設定しているところも注目。

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

3

例外メッセージは丁寧に

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

例外メッセージは丁寧に

何がいけないのかをちゃんと説明する

例: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; }

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

4

メソッドは短くシンプルに

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

メソッドは短くシンプルに例: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

23

45

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

中級編5~8

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

5

パフォーマンスコンシャス

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

パフォーマンスコンシャス例: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しない• 極力インスタンス変数にアクセスしない

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

パフォーマンスコンシャス(拡大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. ループの中でインスタンス変数にアクセスしない

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

パフォーマンスコンシャス(拡大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しない• 極力インスタンス変数にアクセスしない

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

6CloneNotSupportedException

の処理

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

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); }

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

ちなみにhttp://bugs.java.com/bugdatabase/view_bug.do?bug_id=4220218

JDK-4220218 : Please make CloneNotSupportedException uncheckedResolution : Won't Fix

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

7

デザインパターンの適用

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

デザインパターンの適用

↑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)); }

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

8サブクラスで

スーパークラスに定義された型を

狭める

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

サブクラスでスーパークラスに定義された型を狭める

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"); }

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

上級編9~10

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

9

立つ鳥跡を濁さず

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

立つ鳥跡を濁さず例: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インスタンスがお掃除されるとき。このときにちゃ

んと(中で起動している)スレッドを終了させるための仕掛け。

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

10

final変数への代入

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

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); } : } よい子は真似してはいけません!

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

最後に

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

お願い

いいものを見つけたら、 Twitter: @fukushiwまで何卒ご一報下さい!