java puzzlers jjug ccc 2016
TRANSCRIPT
Java Puzzlers
櫻庭 祐一
寺田 佳央
Dedicated to John Carpenter
We are Click and Hack the Type-It brothers !!
コード中に発生する勘違い Java プログラミングにおける 奇妙な振る舞いをする小さなプログラム 複数の選択肢から、何が表示される? ミステリーの解明 問題の解決方法 教訓
Java Puzzlers のルール public class JavaPuzzlers { public static void main(String... args) { System.out.println(“Japan Java User Group Presents!”); } }
1.Japan Java User Group Presents!2.Java Puzzlers3.0xCAFEBABE4.その他
1問目
問題1: They Livepublic class TheyLive { public static void main(String... args) { int sum = 0; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { if (i != 0) sum += i / Math.abs(i); } System.out.println(sum); }}
選択肢1: They Live
選択肢1. -12. 03. 14.その他
public class TheyLive { public static void main(String... args) { int sum = 0; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { if (i != 0) sum += i / Math.abs(i); } System.out.println(sum); }}
正解は ?!
解答1: They Live
選択肢1. -12. 03. 14.その他
public class TheyLive { public static void main(String... args) { int sum = 0; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { if (i != 0) sum += i / Math.abs(i); } System.out.println(sum); }}
解説1:何が悪いのか?
-2147483648/abs(-2147483648) = -1-2147483647/abs(-2147483648) = -1
2147483646/abs(2147483646) = +1+)
……
-2
解説1:何が悪いのか?
-2147483648/abs(-2147483648) = +1-2147483647/abs(-2147483648) = -1
2147483646/abs(2147483646) = +1+)
……
0
解決1:どうやって直すのかpublic class TheyLive { public static void main(String... args) { int sum = -1; for (int i = Integer.MIN_VALUE+1; i < Integer.MAX_VALUE; i++) { if (i != 0) sum += i / Math.abs(i); } System.out.println(sum); }}
教訓1:
• 意図しない動作をするメソッドが存在する
• 使用時には必ず Javadoc をチェックしよう
• メソッドを作成する場合には、
想像しやすい動作になるよう心がけよう
2問目
問題2: MarshmallowManpublic class MarshmallowMan{ public static void main(String... args) { int sum = 0; for (int i = 1; i <= 10; i++) { sum += foo(i); } System.out.println(sum); } public static long foo(long l) {return l;}}
選択肢2: MarshmallowManpublic class MarshmallowMan { public static void main(String... args) { int sum = 0; for (int i = 1; i <= 10; i++) { sum += foo(i); } System.out.println(sum); } public static long foo(long l) {return l;}}
選択肢1. 02. 453. 554. コンパイルエ
ラー
正解は ?!
選択肢2: MarshmallowManpublic class Sum { public static void main(String... args) { int sum = 0; for (int i = 1; i <= 10; i++) { sum += foo(i); } System.out.println(sum); } public static long foo(long l) {return l;}}
選択肢1. 02. 453. 554. コンパイルエ
ラー
解説2:
sum = sum + data コンパイルエラー sum += data コンパイルエラーにはならない
int sum = 0;long data = 10;System.out.println(sum += data);
この問題と同じ
解説2:JLS §15.26.2. Compound Assignment Operators
E1 op= E2E1 = (T) ((E1) op (E2)) ※ T は E1 の型
つまりsum = (int) (sum + data)
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26.2
解決2: Java 8 以降での計算処理
IntStream.rangeClosed(1, 10).sum()
桁があふれた際に ArithmeticException を出力
java.lang.StrictMath#addExactjava.lang.Math#addExact
java.util.concurrent.atomic.LongAdderjava.util.concurrent.atomic.DoubleAdder複数スレッドからの更新で Atomic 性を保つ場合AtomicLong よりおすすめ
教訓2:
• 演算子の振る舞いについて正しく理解しましょ
う
• 何が最適な解決方法か考えましょう
3問目
問題3: The Thingpublic class TheThing { public static void main(String... args) { StringBuilder builder = new StringBuilder("J"); builder = builder.append("a"); Stream.of("v", "a") .forEach(builder::append); System.out.println(builder); }}
選択肢3: The Thingpublic class TheThing { public static void main(String... args) { StringBuilder builder = new StringBuilder("J"); builder = builder.append("a"); Stream.of("v", "a") .forEach(builder::append); System.out.println(builder); }}
選択肢1.Ja2.Java3.Compile Error4.Exception
正解は ?!
選択肢3: The Thingpublic class TheThing { public static void main(String... args) { StringBuilder builder = new StringBuilder("J"); builder = builder.append("a"); Stream.of("v", "a") .forEach(builder::append); System.out.println(builder); }}
選択肢1.Ja2.Java3.Compile Error4.Exception
解説3:何が悪いのか?ラムダ式 :final 以外のローカル変数にアクセスできない
StringBuilder builder = new StringBuilder("J");builder = builder.append("a");
// NG Stream.of("v", "a") .forEach(c -> builder.append(c));
解説3:何が悪いのか?ラムダ式 :final 以外のローカル変数にアクセスできない
// Effectively FinalStringBuilder builder = new StringBuilder("Ja");
// OK Stream.of("v", "a") .forEach(c -> builder.append(c));
解説3:何が悪いのか?メソッド参照 :final でないローカル変数にアクセスできるい
StringBuilder builder = new StringBuilder("J");builder = builder.append("a"); // Not-Final
// OKStream.of("v", "a") .forEach(builder::append);
教訓3:
• ラムダ式とメソッド参照はほぼ同じだが、若干の違
いがあることを意識しよう
• ラムダ式と匿名クラスも違いがあるので、気をつけ
よう
• this の扱いが異なる
• メソッド参照の使いすぎに注意しよう
• 他の人が読めないコードになりがち
4問目
問題4: OpenSesame public class OpenSesame { public static void main(String... argv){ List<Integer> integerList = Arrays.asList(1,2,3,4,5); List<Integer> filterdData = integerList.stream() .filter((int i) -> (i > 2)) .collect(Collectors.toList()); System.out.println(filterdData); } }
選択肢4: OpenSesame public class OpenSesame { public static void main(String... argv){ List<Integer> integerList = Arrays.asList(1,2,3,4,5); List<Integer> filterdData = integerList.stream() .filter((int i) -> (i > 2)) .collect(Collectors.toList()); System.out.println(filterdData); } }
選択肢1. 1,22. 3,4,53. 何も表示されない4. コンパイルエラー
正解は ?!
解答4: OpenSesame public class OpenSesame { public static void main(String... argv){ List<Integer> integerList = Arrays.asList(1,2,3,4,5); List<Integer> filterdData = integerList.stream() .filter((int i) -> (i > 2)) .collect(Collectors.toList()); System.out.println(filterdData); } }
選択肢1. 1,22. 3,4,53. 何も表示されない4. コンパイルエラー
解説4:何が悪いのか? public class OpenSesame { public static void main(String... argv){ List<Integer> integerList = Arrays.asList(1,2,3,4,5); List<Integer> filterdData = integerList.stream() .filter((int i) -> (i > 2)) .collect(Collectors.toList()); System.out.println(filterdData); } }
期待する型が見つからない
クラス Test のメソッド filter は指定された型に適用できません 期待値: List<T>, Predicate<T> 検出値: List, (int i) -> (i>2)
解決4: IntStream を使いましょう public class OpenSesame { public static void main(String... argv){ int[] filterdData = IntStream.range(1, 6) .filter(i -> i > 2) .toArray(); System.out.println(Arrays.toString(filterdData)); } }
教訓4:
• Lambda 式を記述する場合、部分的に型を
書くくらいならば 型を省略しましょう
• primitive 型を使用する場合、対応する
Stream を使いましょう
5問目
問題5: The Fogclass Ship<T> { private T t; public Ship(T t) { this.t = t; } public void setCrew(T t) { this.t = t; } public int hashCode() { return Objects.hashCode(t); }}public class TheFog { public static void main(String... args) { Map<Ship<String>, String> map = new HashMap<>(); Ship<String> a = new Ship<>("A"); Ship<String> b = new Ship<>("B"); map.put(a, "a"); a.setCrew("B"); map.put(b, "b"); System.out.println(map.values().size()); }}
選択肢5: The Fogclass Ship<T> { private T t; public Ship(T t) { this.t = t; } public void setCrew(T t) { this.t = t; } public int hashCode() { return Objects.hashCode(t); }}public class TheFog { public static void main(String... args) { Map<Ship<String>, String> map = new HashMap<>(); Ship<String> a = new Ship<>("A"); Ship<String> b = new Ship<>("B"); map.put(a, "a"); a.setCrew("B"); map.put(b, "b"); System.out.println(map.values().size()); }}
選択肢1. 02. 13. 24. Exception
正解は ?!
解答5: The Fogclass Ship<T> { private T t; public Ship(T t) { this.t = t; } public void setCrew(T t) { this.t = t; } public int hashCode() { return Objects.hashCode(t); }}public class TheFog { public static void main(String... args) { Map<Ship<String>, String> map = new HashMap<>(); Ship<String> a = new Ship<>("A"); Ship<String> b = new Ship<>("B"); map.put(a, "a"); a.setCrew("B"); map.put(b, "b"); System.out.println(map.values().size()); }}
選択肢1. 02. 13. 24. Exception
解説5:
• Map は Key のハッシュ値を使用する
• 同じハッシュ値を put すると、上書きする
• ハッシュ値が後から変更された場合、同じハッシュ値の
エントリがあっても存在し続ける
• しかし、そのエントリにはアクセスできない
• Mutable なオブジェクトを Map のキーにすることは
危険
解決5class Ship<T> { private T t; public Ship(T t) { this.t = t; }// public void setCrew(T t) { this.t = t; } public int hashCode() { return Objects.hashCode(t); }}public class TheFog { public static void main(String... args) { Map<Ship<String>, String> map = new HashMap<>(); Ship<String> a = new Ship<>("A"); Ship<String> b = new Ship<>("B"); map.put(a, "a"); // a.setCrew("B"); map.put(b, "b"); System.out.println(map.values().size()); }}
教訓5:
• Mutable なオブジェクトを Map のキーにしない
• Imutable なオブジェクトを使い慣れよう
• hashCode メソッドをオーバーライドする場合、必
ず equals メソッドもオーバーライドする
Java Puzzlers
櫻庭 祐一
寺田 佳央
Dedicated to John Carpenter