Upload
-
View
1.210
Download
0
Embed Size (px)
Citation preview
from old Java to modern Java
~ レビューで学ぶJava8時代のコーディング作法
Acroquest Technology株式会社 JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
自己紹介• 名前 : 谷本 心 (たにもと しん)
• 職業 : Javaエンジニア / トラブルシューター トラブルシュート教育も引き受けます!
• Twitter : @cero_t
• その他 : 日本Javaユーザ会(JJUG) 幹事 関西Javaエンジニアの会 主催
さて、Java8が出ましたが
今日は関ジャバ∞と考えて
差し支えないですか?
バ
自己紹介• 名前 : 谷本 心 (たにもと しん)
• 職業 : Javaエンジニア / トラブルシューター トラブルシュート教育も引き受けます!
• Twitter : @cero_t
• その他 : 日本Javaユーザ会(JJUG) 幹事 関西Javaエンジニアの会 主催
さて、Java8が出ましたが
「どうせ、使うのはまだ先」とか思ってませんか?
きっと、ずっと先でしょう。
でも、たとえば今
Java6やJava7の勉強をきちんとできますか?
出始めこそ、たくさん情報が出てくる
じゃ、いつやるか?
やらせねーよ (我が家)
from old Java to modern Java
~ レビューで学ぶJava8時代のコーディング作法
Acroquest Technology株式会社 JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
ソースコードレビューしてますか?
レビューをする方が多いですか?受ける方が多いですか?
もちろんレビューする方が多いですね
若いんで(?)レビューを受ける方が
多いです
レビューなんて文化 ありません
履歴書の送付先 :[email protected]
では早速、コード。
Map<Dept, Long> groupByDeptAndFilter(List<Emp> list) { return list.stream() .collect(Collectors.groupingBy(emp -> emp.dept)) .entrySet() .stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue() .stream() .filter(emp -> emp.sal > 1000) .count())); }
何これ読めない
「よく分からないしfor文とmapで
書き直してくれる?」
Map<Dept, Long> groupByDeptClassic(List<Emp> list) { Map<Dept, Long> result = new HashMap<>(); for (Emp emp : list) { if (result.containsKey(emp.dept) == false) { result.put(emp.dept, 0L); }
if (emp.sal > 1000) { Long count = result.get(emp.dept); count++; result.put(emp.dept, count); } }
return result; }
「あー、読みやすくて安心するー」
Welcome to老 害 世 代
from old Java to modern Java
~ 老害にならないためのJava8入門
Acroquest Technology株式会社 JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
Lesson1 今の時代のファイル操作を
理解せよ!
おさらい: finallyでcloseするという定石
List<String> readFileSE6(String fileName) { List<String> lines = new ArrayList<String>(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(fileName)); String line; while ((line = reader.readLine()) != null) { lines.add("<" + line + ">"); } reader.close(); } catch (IOException ex) { throw new RuntimeException(ex); } return lines; }
これあかんやつや!
List<String> readFileSE6(String fileName) { List<String> lines = new ArrayList<String>(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(fileName)); String line; while ((line = reader.readLine()) != null) { lines.add("<" + line + ">"); } } catch (IOException ex) { throw new RuntimeException(ex); // 例外処理は割愛 } finally { try { if (reader != null) { reader.close(); } } catch (IOException ex) { // この例外は無視する? } } return lines; }
やっぱfinallyでcloseですよね!
finallyでcloseするのは古い定石
(~Java6)
try-with-resourcesでイマドキのJavaに!(Java7~)
List<String> readFileSE7_1(String fileName) { List<String> lines = new ArrayList<>();
try (FileReader in = new FileReader(fileName); BufferedReader reader = new BufferedReader(in)) { String line; while ((line = reader.readLine()) != null) { lines.add("<" + line + ">"); } } catch (IOException ex) { throw new RuntimeException(ex); }
return lines; }
これがtry-with-resources!
でも! 変なところがあるんです。
List<String> readFileSE7_1(String fileName) { List<String> lines = new ArrayList<>();
try (FileReader in = new FileReader(fileName); BufferedReader reader = new BufferedReader(in)) { String line; while ((line = reader.readLine()) != null) { lines.add("<" + line + ">"); } } catch (IOException ex) { throw new RuntimeException(ex); }
return lines; }
!?
List<String> readFileSE7_2(String fileName) { List<String> lines = new ArrayList<>();
Path path = Paths.get(fileName); try (BufferedReader reader = Files.newBufferedReader(path)) { String line; while ((line = reader.readLine()) != null) { lines.add("<" + line + ">"); } } catch (IOException ex) { throw new RuntimeException(ex); }
return lines; }
新定石①: try-with-resourcesと FilesとPathで操作する
(Java7~)
新定石②:ファイルを一気に読むならFiles.readAllLines!
(Java7~)
List<String> readFileSE7_3(String fileName) { try { List<String> lines = Files.readAllLines(Paths.get(fileName));
for (int i = 0; i < lines.size(); i++) { lines.set(i, "<" + lines.get(i) + ">"); }
return lines; } catch (IOException ex) { throw new RuntimeException(ex); } }
一気に読み込むのは便利だけど、ヒープメモリもたくさん使っちゃうし、それぞれの行に対する処理が必要な場合は、どうしてもループを回し直さなきゃいけないよね。
Java8時代は?
List<String> readFileSE8_1(String fileName) { List<String> lines = new ArrayList<>();
try { Files.lines(Paths.get(fileName)) .forEach(s -> lines.add("<" + s + ">")); } catch (IOException ex) { throw new UncheckedIOException(ex); } return lines;}
新定石③?: Files.linesで処理をする
(Java8~)
新定石③: forやwhileを見たらStream APIへの置き換えを考える
List<String> readFileSE8_1(String fileName) { try { return Files.lines(Paths.get(fileName)) .map(s -> "<" + s + ">") .collect(Collectors.toList()); } catch (IOException ex) { throw new UncheckedIOException(ex); } }
外部のオブジェクトを操作しない方が安心。
ちょっとクイズ
void writeSE8_1(String fileName, List<String> lines) { Path path = Paths.get(fileName); try (BufferedWriter writer = Files.newBufferedWriter(path)) { lines.forEach(s -> writer.write("<" + s + ">")); } catch (IOException e) { throw new UncheckedIOException(ex); } }
void writeSE8_1(String fileName, List<String> lines) { Path path = Paths.get(fileName); try (BufferedWriter writer = Files.newBufferedWriter(path)) { lines.forEach(s -> writer.write("<" + s + ">")); } catch (IOException e) { throw new UncheckedIOException(ex); } }
1. ちゃんと出力されるんじゃない? 2. 順番がグチャグチャになりそうだな・・・ 3. コンパイルエラーが起きるよ、これ 4. 実行時例外が起きるよ、これ
void writeSE8_1(String fileName, List<String> lines) { Path path = Paths.get(fileName); try (BufferedWriter writer = Files.newBufferedWriter(path)) { lines.forEach(s -> writer.write("<" + s + ">")); } catch (IOException e) { throw new UncheckedIOException(ex); } }
1. ちゃんと出力されるんじゃない? 2. 順番がグチャグチャになりそうだな・・・ 3. コンパイルエラーが起きるよ、これ 4. 実行時例外が起きるよ、これ
void writeSE8_2(String fileName, List<String> lines) { Path path = Paths.get(fileName); try (BufferedWriter writer = Files.newBufferedWriter(path)) { lines.forEach(s -> { try { writer.write(s); } catch (IOException ex) { throw new UncheckedIOException(ex); } }); } catch (IOException ex) { throw new UncheckedIOException(ex); } }
なんでもかんでもLambdaにすれば良いってもんでもない
Lesson2文字列操作はどう変わる?
String joinSE7(List<String> lines) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < lines.size(); i++) { if (i > 0) { builder.append(","); } builder.append(lines.get(i)); }
return builder.toString(); }
String joinCommons(List<String> lines) { return StringUtils.join(lines, ","); }
String joinGuava(List<String> lines) { return Joiner.on(",").join(lines); }
みんな大好き文字列結合
Commons LangとかGuavaとか使うよねー
String joinSE8(List<String> lines) { return String.join(",", lines); }
ようやく搭載されたString#join
新定石④: String.joinさんこんにちは!(Java8~)
String joinPrefixSE7(List<String> lines) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < lines.size(); i++) { if (i > 0) { builder.append(","); } builder.append(“<") .append(lines.get(i)) .append(">"); }
return builder.toString(); }
じゃ、こういうパターンはどうするの?
private String joinPrefixCommons(List<String> lines) { return "<" + StringUtils.join(lines, ">,<") + ">"; }
private String joinPrefixGuava(List<String> lines) { return "<" + Joiner.on(">,<").join(lines) + ">"; }
助けてJava8マン!
private String joinPrefixSE8(List<String> lines) { return lines.stream() .map(s -> "<" + s +”>") .collect(Collectors.joining(",")); }
要するにStreamAPIが正義
新定石⑤: String.joinさんさようなら!(Java8~)
結局、新定石③: forやwhileを見たらStream APIへの置き換えを考える
Lesson3 forやwhileとifとか
いろいろ行なう集計的なアレ
Map<Dept, Long> groupByDeptAndFilterClassic(List<Emp> list) { Map<Dept, Long> result = new HashMap<>(); for (Emp emp : list) { if (result.containsKey(emp.dept) == false) { result.put(emp.dept, 0L); }
if (emp.sal > 1000) { Long count = result.get(emp.dept) + 1; result.put(emp.dept, count); } }
return result; }
給料が1000ドルを超える社員の数を部署ごとに集計する
Java8時代は、最初、こうなると思う
Map<Dept, Long> groupByDeptAndFilterClassic(List<Emp> list) { Map<Dept, Long> result = new HashMap<>(); for (Emp emp : list) { result.putIfAbsent(emp.dept, 0L);
if (emp.sal > 1000) { Long count = result.get(emp.dept) + 1; result.put(emp.dept, count); } }
return result; }
新しい便利API
でも、新定石③: forやwhileを見たらStream APIへの置き換えを考える
Map<Dept, Long> groupByDeptAndFilterLambda(List<Emp> list) { return list.stream() .collect(Collectors.groupingBy(emp -> emp.dept)) .entrySet() .stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue() .stream() .filter(emp -> emp.sal > 1000) .count())); }
どうやって書くの? どうやって読むの?
select DEPT_ID, COUNT(EMP_ID) from EMP where EMP.SAL > 1000 group by EMP.DEPT_ID
SQLと一緒。勉強して、書いて、書いて、書いて、そして、書く。
input : List<Emp>
output : Map<Dept, Empの数> ただしEmpは給与が1000より大きい
1. いったん List<Emp> を Map<Dept, List<Emp>> にグルーピングする。
2. Mapの値であるList<Emp>を1000でフィルタリングする。
3. フィルタリング後のList<Emp>をカウントする。
Map<Dept, Long> groupByDeptAndFilterLambda2(List<Emp> list) { Map<Dept, List<Emp>> groupByDept = list.stream() .collect(Collectors.groupingBy(emp -> emp.dept));
Map<Dept, List<? super Emp>> filtered = groupByDept.entrySet() .stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue() .stream() .filter(emp -> emp.sal > 1000) .collect(Collectors.toList())));
Map<Dept, Long> counted = filtered.entrySet() .stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue() .stream() .count()));
return counted; }
Map<Dept, Long> groupByDeptAndFilterLambda(List<Emp> list) { return list.stream() .collect(Collectors.groupingBy(emp -> emp.dept)) .entrySet() .stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue() .stream() .filter(emp -> emp.sal > 1000) .count())); }
正直、ちょっとAPIが足りない感じなので自分で追加すると良い(この辺は熟成待ち?)
http://blog.exoego.net/2013/12/control-collector-to-rule-stream-api.html Collectorを征す者はStream APIを征す(部分的に) - I am programmer and proud
検索: ラムダ禁止
まとめ
forとwhileを見たらLambda化を考えよう
レビューでforとwhileを見つけたらLambda化させよう
レビューで100行ぐらいの
Lambdaが出てきたら
…
自分たちの標準としてどうすれば読みやすくするか
チームで考えよう!
from old Java to modern Java
~ 老害にならないためのJava8入門
Acroquest Technology株式会社 JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
あ、そうそう
from old Javato modern Java
がきっかけで
Java本格入門(仮) 鋭意執筆中!
頑張ります m(_ _)m
Let’s Study Java8!