気づけば自分のJava知識が6ぐらいで止まっているので、知識のアップデートがてら最近のJava記法を紹介します。
バージョン
主要なJavaのバージョンとリリース年です。
- Java6: 2006年
- Java8: 2014年
- Java11: 2018年
- Java17: 2021年
Java8, 11が採用される時代であることがわかると思います。
それではJava8, 11で追加された記法・構文についていくつか紹介します。
Java8
ラムダ式
ラムダ式は通常の関数と違い、関数内で変数のように関数処理を定義できます。
public static void main(String[] args){
// 引数、戻り値なし
Runnable hello = () -> { System.out.println("Hello!! こんにちわ!"); };
hello.run(); // Hello!! こんにちわ!
// Function<T, R> Tが引数、Rが戻り値
Function<Integer, String> sharp = (i) -> { return "#"+ i; };
String s = sharp.apply(99); // #99
// Consumer<T> 引数あり、戻り値なし
Consumer<Integer> at = (i) -> { System.out.println("@"+ i); };
at.accept(77); // @77
}
柔軟で便利ですが、まとまった処理なら関数化するなど使い分けが大事そうです。
List/Map forEach
List, MapにforEach()メソッドが追加されました。
// List
for(String name: list){
System.out.println(name);
}
// Map
for(Map.Entry<String, Integer> entry : items.entrySet()){
System.out.println(entry.getValue());
}
↑従来、↓forEach()を使った場合。
// List
list.forEach((name) -> {
System.out.println(name);
});
// Map
items.forEach((key, value)->{
System.out.println(value);
})
何をループさせているのか、わかりやすくなりましたね。
ラムダ式の注意点になるのです。ラムダ式の外側の変数を参照できますが、変更はできないようです。
String appendix = "様";
list.forEach(name -> {
System.out.println(name + appendix); // 参照可能
// appendix = "ちゃん"; // 変更はNG
});
Optional
Optionalは、nullとなるかもしれないケースで役立つクラスです。
public hoge(){
Integer value = this.count("foo");
// nullチェックする
if(value == null){
value = -1;
}
System.out.println(value);
}
private Integer count(string key){
return map.get(key);
}
↑従来、↓Optionalを使った場合。
public hoge(){
Optional<Integer> optionalValue = this.count("foo");
// nullなら-1
Integer value = optionalValue.orElse(-1);
System.out.println(value);
}
// 戻り値をOptionalで包む
private Optional<Integer> count(string key){
return Optional.ofNullable(map.get(key));
}
闇雲に使うと混乱しそうですが、適切に使うと可読性の高いコードになりそうです。
StreamAPI
StreamAPIはループ処理をわかりやすくするもので、データを加工するのに便利です。
List<String> list1 = Arrays.asList("TANAKA", "YAMADA", "SATO");
// SATOを取り除く
List<String> list2 = new ArrayList<>();
for(String name: list1){
if(name.equals("SATO")){
continue;
}
list2.add(name);
}
// 挨拶文を作る
List<String> list3 = new ArrayList<>();
for(String name: list2){
String message = "こんにちは、" + name + "さん。"
list3.add(message);
}
↑従来、↓StreamAPIを使った場合。
List<String> list = Arrays.asList("TANAKA", "YAMADA", "SATO");
List<String> messages = list.stream()
.filter(name -> !name.equals("SATO"))
.map(name -> "こんにちは、" + name + "さん。")
.collect(Collectors.toList());
stream()
でListをStream化filter()
でSATOを取り除くmap()
で挨拶文を作るcollect()
でListに戻す
今まではfor文一つでしていたことが、何のループ処理なのか明示的になりわかりやすくなりました。
Java11
var
ローカル変数の型推論をしてくれます。
Hoge hoge = new Hoge();
List<User> users = List.of(new User("jon"), new User("ben"));
↑従来、↓varを使った場合。
var hoge = new Hoge();
var users = List.of(new User("jon"), new User("ben"));
変数の型を省略できるので可読性が上がります。
try-with-resource
冗長だったリソースのclose()処理を省略することができる構文です。
BufferedReader reader = null;
String line = null;
try{
reader = new BufferedReader(new FileReader("test.txt"));
line = br.readLine();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
if(reader != null){
try{
reader.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
↑従来、↓try-with-resourceの使った場合。
var reader = new BufferedReader(new FileReader("test.txt"));
try(reader){
line = br.readLine();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
try(reader)
とリソースを指定することで、try終了時にclose()を自動でやってくれるので便利ですね。
List/Map of
immutable、つまり変更や追加ができないListやMapを簡単に生成できます。
var os = List.of("win", "mac", "linux");
var conties = Map.of(
"jp", "日本",
"us", "アメリカ");
後から自由に変更されたくないモードや定義値として活用すると、改修に強いコードになりそうです。
色々紹介しましたが、個人的にはStreamAPIを使いこなせるようになってみたいと思いました。
また、来年の2021年には、Java17が出るので引き続き知識のアップデートをしたいと思います。