1
1

More than 3 years have passed since last update.

徹底解説!Java8特性

Last updated at Posted at 2021-08-07

皆さん、こんにちは!

最近、仕事を探しています。java エンジンニアとして面接をうける際に、よくjava8の特性の問題を質問されました。
だから、本記事はjava8の特性を中心としてまとめてご解説いたします。

一、ラムダ式

この前のjavaバージョンでリストをソートするために、下記のように実装すべきである。

List<String> myList= Arrays.asList("tom", "jack", "smith", "allen");
Collections.sort(myList, new Comparator() { 
  @Override 
  public int compare(String a, String b) { 
    return b.compareTo(a); 
  }
});

だが、java8の特性(lambda式)を用いたら、下記のlambda式で簡単に実装できる。

Collections.sort(names, (a, b) -> b.compareTo(a));

上記のlambda式で、コードはめっちゃ読みやすいでしょうかね。
なお、a,bの型はjvmにより、自動的に推測できるし、returnキーワードもなくなった。

二、Functional Interface

Functional Interfaceとは

interfaceの中に一個だけのメソッドがある場合のみ、lambda式に書き換える。
例えば、あるinterfaceは以下のようにします。@FunctionalInterfaceアノテーションをつけると、Functional Interfaceのことを明らかにする。

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

次に、以下のようにlambada式に書き換えましょうか。

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

Predicateインターフェース

Predicateとは、引数を指定してboolean型の値を戻すインターフェースである。
下記の使い方をご参考しましょう。

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("zoo");              // true
predicate.negate().test("zoo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

Functionインターフェース

Functionとは、Functionインターフェースを使うと引数に基づいてにインスタンスを戻れます。

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Supplierインターフェース

Supplierとは、Supplierインターフェースを使うと引数を指定せずに、インスタンスを戻れます。

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

Consumerインターフェース

Consumerとは、引数を指定するうえで、次の処理を行う。

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.lastName);
greeter.accept(new Person("Tom", "Smith"));

Comparatorインターフェース

Comparatorについては、この前にも存在しているけど、java8以降、lamba式に変更されます。

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0

Optional

Optionalとは、 Functional Interfaceじゃなくて、NullPointerExceptionを防ぐために、よく使われています。
Optionalの使い方は下記のようにご参考しましょう。

Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0))); 

三、Stream API

Streamとは、集合に対する処理機能を提供するAPI。

ここで、説明用のリストを予め作ります。

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");

Filterの使い方

条件を満たすデータしか返らない。

stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);
// "aaa2", "aaa1"

Sortedの使い方

Streamにあるデータをソートする。
Comparatorを指定しないとディフォルトのを使います。

stringCollection
    .stream()
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Mapの使い方

Streamの中身を変換する。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Matchの使い方

Streamの中身はある条件にマッチするかどうかを判断する。

// リストの中に先頭が"a"のstringがあるかどうかを判断する。あれば、trueを返す。
boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

// リストの中にあらゆるデータが"a"で先頭であるかどうかを判断する。
boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

Countの使い方

Streamにあるデータの数をカウントする。

long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

System.out.println(startsWithB);    // 3

Reduceの使い方

Streamにあるデータを一つにする。

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

四、最後に

他には、java8は新たな日付API(ZoneId、LocalDate 、LocalDateTime など)を提供するが、ここで略です。

最後まで読んでいただき、ありがとうございます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1