Upload
justsystems-corporation
View
4.427
Download
0
Embed Size (px)
Citation preview
Javaチョットデキルへの道~JavaコアSDKに見る真似したいコード10選~
JJUG CCC 2017 Spring2017/05/20#jjug_ccc
#ccc_l7Made with
エルナナ
自己紹介
• 株式会社ジャストシステム 福嶋 航• Twitter @fukushiw• Java歴約20年、JavaでWebサービス作っています• #Java100 本ノックの人https://github.com/JustSystems/java-100practices
今日お話しすること
無料でJava力アップ!
JavaコアAPIのソースはまさに宝の山。Java 100本ノック作者がその中から選りすぐりの
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
本題
初級編1~4
1
NullPointerException
ダメ。ゼッタイ。
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; }
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); }
2
ガード節
ガード節例: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); }
典型的なガード節と例外スロー。その後の処理ロジックで異常な状態を考慮しなくてよいようにす
ることで可読性を向上している。例外スロー時にちゃんとメッセージを設定しているところも注目。
3
例外メッセージは丁寧に
例外メッセージは丁寧に
何がいけないのかをちゃんと説明する
例: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; }
4
メソッドは短くシンプルに
メソッドは短くシンプルに例: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
中級編5~8
5
パフォーマンスコンシャス
パフォーマンスコンシャス例: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しない• 極力インスタンス変数にアクセスしない
パフォーマンスコンシャス(拡大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. ループの中でインスタンス変数にアクセスしない
パフォーマンスコンシャス(拡大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しない• 極力インスタンス変数にアクセスしない
6CloneNotSupportedException
の処理
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); }
ちなみにhttp://bugs.java.com/bugdatabase/view_bug.do?bug_id=4220218
JDK-4220218 : Please make CloneNotSupportedException uncheckedResolution : Won't Fix
7
デザインパターンの適用
デザインパターンの適用
↑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)); }
8サブクラスで
スーパークラスに定義された型を
狭める
サブクラスでスーパークラスに定義された型を狭める
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"); }
上級編9~10
9
立つ鳥跡を濁さず
立つ鳥跡を濁さず例: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インスタンスがお掃除されるとき。このときにちゃ
んと(中で起動している)スレッドを終了させるための仕掛け。
10
final変数への代入
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); } : } よい子は真似してはいけません!
最後に
お願い
いいものを見つけたら、 Twitter: @fukushiwまで何卒ご一報下さい!