48
Java Puzzlers 櫻櫻 櫻櫻櫻 櫻櫻 Dedicated to John Carpenter

Java Puzzlers JJUG CCC 2016

Embed Size (px)

Citation preview

Page 1: Java Puzzlers JJUG CCC 2016

Java Puzzlers

櫻庭 祐一

寺田 佳央

Dedicated to John Carpenter

Page 2: Java Puzzlers JJUG CCC 2016

We are Click and Hack the Type-It brothers !!

Page 3: Java Puzzlers JJUG CCC 2016

コード中に発生する勘違い Java プログラミングにおける  奇妙な振る舞いをする小さなプログラム  複数の選択肢から、何が表示される?  ミステリーの解明  問題の解決方法  教訓

Page 4: Java Puzzlers JJUG CCC 2016

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.その他

Page 5: Java Puzzlers JJUG CCC 2016

1問目

Page 6: Java Puzzlers JJUG CCC 2016

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

Page 7: Java Puzzlers JJUG CCC 2016

選択肢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); }}

Page 8: Java Puzzlers JJUG CCC 2016

正解は ?!

Page 9: Java Puzzlers JJUG CCC 2016

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

Page 10: Java Puzzlers JJUG CCC 2016

解説1:何が悪いのか?

-2147483648/abs(-2147483648) = -1-2147483647/abs(-2147483648) = -1

2147483646/abs(2147483646) = +1+)

……

-2

Page 11: Java Puzzlers JJUG CCC 2016

解説1:何が悪いのか?

-2147483648/abs(-2147483648) = +1-2147483647/abs(-2147483648) = -1

2147483646/abs(2147483646) = +1+)

……

0

Page 12: Java Puzzlers JJUG CCC 2016

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

Page 13: Java Puzzlers JJUG CCC 2016

教訓1:

• 意図しない動作をするメソッドが存在する

• 使用時には必ず Javadoc をチェックしよう

• メソッドを作成する場合には、

想像しやすい動作になるよう心がけよう

Page 14: Java Puzzlers JJUG CCC 2016

2問目

Page 15: Java Puzzlers JJUG CCC 2016

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

Page 16: Java Puzzlers JJUG CCC 2016

選択肢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. コンパイルエ

ラー

Page 17: Java Puzzlers JJUG CCC 2016

正解は ?!

Page 18: Java Puzzlers JJUG CCC 2016

選択肢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. コンパイルエ

ラー

Page 19: Java Puzzlers JJUG CCC 2016

解説2:

sum = sum + data コンパイルエラー sum += data コンパイルエラーにはならない

int sum = 0;long data = 10;System.out.println(sum += data);

この問題と同じ

Page 20: Java Puzzlers JJUG CCC 2016

解説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

Page 21: Java Puzzlers JJUG CCC 2016

解決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 よりおすすめ

Page 22: Java Puzzlers JJUG CCC 2016

教訓2:

• 演算子の振る舞いについて正しく理解しましょ

• 何が最適な解決方法か考えましょう

Page 23: Java Puzzlers JJUG CCC 2016

3問目

Page 24: Java Puzzlers JJUG CCC 2016

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

Page 25: Java Puzzlers JJUG CCC 2016

選択肢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

Page 26: Java Puzzlers JJUG CCC 2016

正解は ?!

Page 27: Java Puzzlers JJUG CCC 2016

選択肢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

Page 28: Java Puzzlers JJUG CCC 2016

解説3:何が悪いのか?ラムダ式 :final 以外のローカル変数にアクセスできない

StringBuilder builder = new StringBuilder("J");builder = builder.append("a");

// NG Stream.of("v", "a") .forEach(c -> builder.append(c));

Page 29: Java Puzzlers JJUG CCC 2016

解説3:何が悪いのか?ラムダ式 :final 以外のローカル変数にアクセスできない

// Effectively FinalStringBuilder builder = new StringBuilder("Ja");

// OK Stream.of("v", "a") .forEach(c -> builder.append(c));

Page 30: Java Puzzlers JJUG CCC 2016

解説3:何が悪いのか?メソッド参照 :final でないローカル変数にアクセスできるい

StringBuilder builder = new StringBuilder("J");builder = builder.append("a"); // Not-Final

// OKStream.of("v", "a") .forEach(builder::append);

Page 31: Java Puzzlers JJUG CCC 2016

教訓3:

• ラムダ式とメソッド参照はほぼ同じだが、若干の違

いがあることを意識しよう

• ラムダ式と匿名クラスも違いがあるので、気をつけ

よう

• this の扱いが異なる

• メソッド参照の使いすぎに注意しよう

• 他の人が読めないコードになりがち

Page 32: Java Puzzlers JJUG CCC 2016

4問目

Page 33: Java Puzzlers JJUG CCC 2016

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

Page 34: Java Puzzlers JJUG CCC 2016

選択肢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. コンパイルエラー

Page 35: Java Puzzlers JJUG CCC 2016

正解は ?!

Page 36: Java Puzzlers JJUG CCC 2016

解答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. コンパイルエラー

Page 37: Java Puzzlers JJUG CCC 2016

解説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)

Page 38: Java Puzzlers JJUG CCC 2016

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

Page 39: Java Puzzlers JJUG CCC 2016

教訓4:

• Lambda 式を記述する場合、部分的に型を

 書くくらいならば 型を省略しましょう

• primitive 型を使用する場合、対応する

Stream を使いましょう

Page 40: Java Puzzlers JJUG CCC 2016

5問目

Page 41: Java Puzzlers JJUG CCC 2016

問題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()); }}

Page 42: Java Puzzlers JJUG CCC 2016

選択肢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

Page 43: Java Puzzlers JJUG CCC 2016

正解は ?!

Page 44: Java Puzzlers JJUG CCC 2016

解答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

Page 45: Java Puzzlers JJUG CCC 2016

解説5:

• Map は Key のハッシュ値を使用する

• 同じハッシュ値を put すると、上書きする

• ハッシュ値が後から変更された場合、同じハッシュ値の

エントリがあっても存在し続ける

• しかし、そのエントリにはアクセスできない

• Mutable なオブジェクトを Map のキーにすることは

危険

Page 46: Java Puzzlers JJUG CCC 2016

解決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()); }}

Page 47: Java Puzzlers JJUG CCC 2016

教訓5:

• Mutable なオブジェクトを Map のキーにしない

• Imutable なオブジェクトを使い慣れよう

• hashCode メソッドをオーバーライドする場合、必

ず equals メソッドもオーバーライドする

Page 48: Java Puzzlers JJUG CCC 2016

Java Puzzlers

櫻庭 祐一

寺田 佳央

Dedicated to John Carpenter