Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 Us...
1問目
問題1:They Live
public class TheyLive {
public static void main(String... args) {
int sum = 0;
for (int i = Integer.MIN_VALU...
選択肢1:They Live
選択肢
1. -1
2. 0
3. 1
4. その他
public class TheyLive {
public static void main(String... args) {
int sum = 0;
f...
正解は ?!
解答1:They Live
選択肢
1. -1
2. 0
3. 1
4. その他
public class TheyLive {
public static void main(String... args) {
int sum = 0;
fo...
解説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_VAL...
教訓1:
• 意図しない動作をするメソッドが存在する
• 使用時には必ずJavadocをチェックしよう
• メソッドを作成する場合には、
想像しやすい動作になるよう心がけよう
2問目
問題2:MarshmallowMan
public class MarshmallowMan{
public static void main(String... args) {
int sum = 0;
for (int i = 1; i <...
選択肢2:MarshmallowMan
public class MarshmallowMan {
public static void main(String... args) {
int sum = 0;
for (int i = 1; i...
正解は ?!
選択肢2:MarshmallowMan
public class Sum {
public static void main(String... args) {
int sum = 0;
for (int i = 1; i <= 10; i++...
解説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= E2
E1 = (T) ((E1) op (E2)) ※ T は E1 の型
つまり
sum = (int) (sum + data...
解決2:Java 8 以降での計算処理
IntStream.rangeClosed(1, 10).sum()
桁があふれた際に ArithmeticException を出力
java.lang.StrictMath#addExact
java...
教訓2:
• 演算子の振る舞いについて正しく理解しましょう
• 何が最適な解決方法か考えましょう
3問目
問題3:The Thing
public class TheThing {
public static void main(String... args) {
StringBuilder builder
= new StringBuilder(...
選択肢3:The Thing
public class TheThing {
public static void main(String... args) {
StringBuilder builder
= new StringBuilder...
正解は ?!
選択肢3:The Thing
public class TheThing {
public static void main(String... args) {
StringBuilder builder
= new StringBuilder...
解説3:何が悪いのか?
ラムダ式:
final 以外のローカル変数にアクセスできない
StringBuilder builder = new StringBuilder("J");
builder = builder.append("a");
...
解説3:何が悪いのか?
ラムダ式:
final 以外のローカル変数にアクセスできない
// Effectively Final
StringBuilder builder = new StringBuilder("Ja");
// OK
Str...
解説3:何が悪いのか?
メソッド参照:
finalでないローカル変数にアクセスできるい
StringBuilder builder = new StringBuilder("J");
builder = builder.append("a");...
教訓3:
• ラムダ式とメソッド参照はほぼ同じだが、若干の違
いがあることを意識しよう
• ラムダ式と匿名クラスも違いがあるので、気をつけ
よう
• thisの扱いが異なる
• メソッド参照の使いすぎに注意しよう
• 他の人が読めないコードにな...
4問目
問題4:OpenSesame
public class OpenSesame {
public static void main(String... argv){
List<Integer> integerList = Arrays.asLis...
選択肢4:OpenSesame
public class OpenSesame {
public static void main(String... argv){
List<Integer> integerList = Arrays.asLi...
正解は ?!
解答4:OpenSesame
public class OpenSesame {
public static void main(String... argv){
List<Integer> integerList = Arrays.asLis...
解説4:何が悪いのか?
public class OpenSesame {
public static void main(String... argv){
List<Integer> integerList = Arrays.asList(1...
解決4:IntStream を使いましょう
public class OpenSesame {
public static void main(String... argv){
int[] filterdData =
IntStream.ran...
教訓4:
• Lambda 式を記述する場合、部分的に型を
書くくらいならば 型を省略しましょう
• primitive 型を使用する場合、対応する
Stream を使いましょう
5問目
問題5:The Fog
class Ship<T> {
private T t;
public Ship(T t) { this.t = t; }
public void setCrew(T t) { this.t = t; }
public ...
選択肢5:The Fog
class Ship<T> {
private T t;
public Ship(T t) { this.t = t; }
public void setCrew(T t) { this.t = t; }
public...
正解は ?!
解答5:The Fog
class Ship<T> {
private T t;
public Ship(T t) { this.t = t; }
public void setCrew(T t) { this.t = t; }
public ...
解説5:
• MapはKeyのハッシュ値を使用する
• 同じハッシュ値をputすると、上書きする
• ハッシュ値が後から変更された場合、同じハッシュ値の
エントリがあっても存在し続ける
• しかし、そのエントリにはアクセスできない
• Muta...
解決5class Ship<T> {
private T t;
public Ship(T t) { this.t = t; }
// public void setCrew(T t) { this.t = t; }
public int ha...
教訓5:
• MutableなオブジェクトをMapのキーにしない
• Imutableなオブジェクトを使い慣れよう
• hashCodeメソッドをオーバーライドする場合、必ず
equalsメソッドもオーバーライドする
Java
Puzzlers
櫻庭 祐一
寺田 佳央
Dedicated to John
Carpenter
Upcoming SlideShare
Loading in …5
×

Java Puzzlers JJUG CCC 2016

4,158 views

Published on

This is the contents of the Java Puzzlers session at JJUG CCC 2016.

Published in: Technology
  • Be the first to comment

Java Puzzlers JJUG CCC 2016

  1. 1. Java Puzzlers 櫻庭 祐一 寺田 佳央 Dedicated to John Carpenter
  2. 2. We are Click and Hack the Type-It brothers !!
  3. 3. コード中に発生する勘違い Java プログラミングにおける 奇妙な振る舞いをする小さなプログラム 複数の選択肢から、何が表示される? ミステリーの解明 問題の解決方法 教訓
  4. 4. 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 Puzzlers 3.0xCAFEBABE 4.その他
  5. 5. 1問目
  6. 6. 問題1:They Live 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); } }
  7. 7. 選択肢1:They Live 選択肢 1. -1 2. 0 3. 1 4. その他 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); } }
  8. 8. 正解は ?!
  9. 9. 解答1:They Live 選択肢 1. -1 2. 0 3. 1 4. その他 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); } }
  10. 10. 解説1:何が悪いのか? -2147483648/abs(-2147483648) = -1 -2147483647/abs(-2147483648) = -1 2147483646/abs(2147483646) = +1+) …… -2
  11. 11. 解説1:何が悪いのか? -2147483648/abs(-2147483648) = +1 -2147483647/abs(-2147483648) = -1 2147483646/abs(2147483646) = +1+) …… 0
  12. 12. 解決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); } }
  13. 13. 教訓1: • 意図しない動作をするメソッドが存在する • 使用時には必ずJavadocをチェックしよう • メソッドを作成する場合には、 想像しやすい動作になるよう心がけよう
  14. 14. 2問目
  15. 15. 問題2:MarshmallowMan public 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;}}
  16. 16. 選択肢2:MarshmallowMan public 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. 0 2. 45 3. 55 4. コンパイルエラー
  17. 17. 正解は ?!
  18. 18. 選択肢2:MarshmallowMan public 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. 0 2. 45 3. 55 4. コンパイルエラー
  19. 19. 解説2: sum = sum + data コンパイルエラー sum += data コンパイルエラーにはならない int sum = 0; long data = 10; System.out.println(sum += data); この問題と同じ
  20. 20. 解説2: JLS §15.26.2. Compound Assignment Operators E1 op= E2 E1 = (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
  21. 21. 解決2:Java 8 以降での計算処理 IntStream.rangeClosed(1, 10).sum() 桁があふれた際に ArithmeticException を出力 java.lang.StrictMath#addExact java.lang.Math#addExact java.util.concurrent.atomic.LongAdder java.util.concurrent.atomic.DoubleAdder 複数スレッドからの更新で Atomic 性を保つ場合 AtomicLong よりおすすめ
  22. 22. 教訓2: • 演算子の振る舞いについて正しく理解しましょう • 何が最適な解決方法か考えましょう
  23. 23. 3問目
  24. 24. 問題3:The Thing public 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); } }
  25. 25. 選択肢3:The Thing public 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. Ja 2. Java 3. Compile Error 4. Exception
  26. 26. 正解は ?!
  27. 27. 選択肢3:The Thing public 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. Ja 2. Java 3. Compile Error 4. Exception
  28. 28. 解説3:何が悪いのか? ラムダ式: final 以外のローカル変数にアクセスできない StringBuilder builder = new StringBuilder("J"); builder = builder.append("a"); // NG Stream.of("v", "a") .forEach(c -> builder.append(c));
  29. 29. 解説3:何が悪いのか? ラムダ式: final 以外のローカル変数にアクセスできない // Effectively Final StringBuilder builder = new StringBuilder("Ja"); // OK Stream.of("v", "a") .forEach(c -> builder.append(c));
  30. 30. 解説3:何が悪いのか? メソッド参照: finalでないローカル変数にアクセスできるい StringBuilder builder = new StringBuilder("J"); builder = builder.append("a"); // Not-Final // OK Stream.of("v", "a") .forEach(builder::append);
  31. 31. 教訓3: • ラムダ式とメソッド参照はほぼ同じだが、若干の違 いがあることを意識しよう • ラムダ式と匿名クラスも違いがあるので、気をつけ よう • thisの扱いが異なる • メソッド参照の使いすぎに注意しよう • 他の人が読めないコードになりがち
  32. 32. 4問目
  33. 33. 問題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); } }
  34. 34. 選択肢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,2 2. 3,4,5 3. 何も表示されない 4. コンパイルエラー
  35. 35. 正解は ?!
  36. 36. 解答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,2 2. 3,4,5 3. 何も表示されない 4. コンパイルエラー
  37. 37. 解説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)
  38. 38. 解決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)); } }
  39. 39. 教訓4: • Lambda 式を記述する場合、部分的に型を 書くくらいならば 型を省略しましょう • primitive 型を使用する場合、対応する Stream を使いましょう
  40. 40. 5問目
  41. 41. 問題5:The Fog class 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()); }}
  42. 42. 選択肢5:The Fog class 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. 0 2. 1 3. 2 4. Exception
  43. 43. 正解は ?!
  44. 44. 解答5:The Fog class 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. 0 2. 1 3. 2 4. Exception
  45. 45. 解説5: • MapはKeyのハッシュ値を使用する • 同じハッシュ値をputすると、上書きする • ハッシュ値が後から変更された場合、同じハッシュ値の エントリがあっても存在し続ける • しかし、そのエントリにはアクセスできない • MutableなオブジェクトをMapのキーにすることは危険
  46. 46. 解決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()); }}
  47. 47. 教訓5: • MutableなオブジェクトをMapのキーにしない • Imutableなオブジェクトを使い慣れよう • hashCodeメソッドをオーバーライドする場合、必ず equalsメソッドもオーバーライドする
  48. 48. Java Puzzlers 櫻庭 祐一 寺田 佳央 Dedicated to John Carpenter

×