SlideShare a Scribd company logo
from old Java

to modern Java
∼ レビューで学ぶJava8時代のコーディング作法
Acroquest Technology株式会社

JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
自己紹介
• 名前 : 谷本 心 (たにもと しん)
• 職業 : Javaエンジニア / トラブルシューター

   トラブルシュート教育も引き受けます!
• Twitter : @cero_t
• その他 : 日本Javaユーザ会(JJUG) 幹事

    関西Javaエンジニアの会 主催
さて、

Java8が出ましたが
今日は

関ジャバ∞

と考えて

差し支えないですか?
バ
from old java to java8 - KanJava Edition
from old java to java8 - KanJava Edition
自己紹介
• 名前 : 谷本 心 (たにもと しん)
• 職業 : Javaエンジニア / トラブルシューター

   トラブルシュート教育も引き受けます!
• Twitter : @cero_t
• その他 : 日本Javaユーザ会(JJUG) 幹事

    関西Javaエンジニアの会 主催
さて、

Java8が出ましたが
「どうせ、使うのはまだ先」

とか思ってませんか?
きっと、ずっと先でしょう。
でも、たとえば今
Java6やJava7の勉強を

きちんとできますか?
出始めこそ、

たくさん情報が出てくる
じゃ、いつやるか?
やらせねーよ
(我が家)
from old Java

to modern Java
∼ レビューで学ぶJava8時代のコーディング作法
Acroquest Technology株式会社

JJUG / 関西Javaエンジニアの会
谷本 心 ( @cero_t )
ソースコードレビュー

してますか?
レビューを

する方が多いですか?

受ける方が多いですか?
もちろん

レビューする方が

多いですね
若いんで(?)

レビューを受ける方が

多いです
レビューなんて文化

ありません
履歴書の送付先 :

tanimoto.shin@gmail.com
では早速、コード。
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 Java

to modern Java
がきっかけで
Java本格入門(仮)
鋭意執筆中!
頑張ります
m(_ _)m
Let s Study Java8!

More Related Content

from old java to java8 - KanJava Edition