インターフェースの応用とLambda

このレッスンでは、インターフェースの高度な機能とLambda式を学び、コードをより簡潔にします。

インターフェースのデフォルトメソッド

Java 8では、インターフェースがメソッド本体を持てるようにするデフォルトメソッドが導入されました。

構文

JAVA
public interface MyInterface {
    // 抽象メソッド
    void abstractMethod();
    
    // デフォルトメソッド
    default void defaultMethod() {
        System.out.println("デフォルト実装");
    }
}

例:デフォルトメソッド

JAVA
public interface Vehicle {
    void start();
    
    default void horn() {
        System.out.println("ビープ!");
    }
    
    default void stop() {
        System.out.println("車両停止");
    }
}

public class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("車が起動");
    }
    
    // デフォルトメソッドをオプションでオーバーライド可能
    @Override
    public void horn() {
        System.out.println("クラクション!");
    }
}

public class DefaultMethodDemo {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();  // 車が起動
        car.horn();   // クラクション!(オーバーライドされたメソッド)
        car.stop();   // 車車両停止(インターフェースのデフォルト)
    }
}
▶ 試してみよう

インターフェースの静的メソッド

Java 8では、インターフェース名を通じて直接呼び出される静的メソッドが導入されました。

JAVA
public interface MathUtils {
    static int add(int a, int b) {
        return a + b;
    }
    
    static int multiply(int a, int b) {
        return a * b;
    }
}

public class StaticMethodDemo {
    public static void main(String[] args) {
        System.out.println(MathUtils.add(3, 5));      // 8
        System.out.println(MathUtils.multiply(3, 5));  // 15
    }
}

関数型インターフェース

抽象メソッドが1つだけのインターフェースは、Lambda式で実装できます。

@FunctionalInterfaceアノテーション

JAVA
@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

一般的な関数型インターフェース

インターフェース メソッド 説明
Predicate<T> boolean test(T t) 条件をテスト
Function<T,R> R apply(T t) 入力を出力に変換
Consumer<T> void accept(T t) 入力を消費
Supplier<T> T get() 出力を提供

Lambda式

Lambdaは無名関数の短縮形で、Java 8で導入されました。

構文

JAVA
(パラメータ) -> { 本体 }

例:Lambda

JAVA
// 従来の方法
Calculator add = new Calculator() {
    @Override
    public int calculate(int a, int b) {
        return a + b;
    }
};

// Lambda式
Calculator add = (a, b) -> a + b;

// 呼び出し
int result = add.calculate(3, 5);  // 8
▶ 試してみよう

さまざまなLambda形式

JAVA
// パラメータなし
Runnable r = () -> System.out.println("Hello");

// 1つのパラメータ
Consumer<String> print = s -> System.out.println(s);

// 複数のパラメータ
Comparator<String> comp = (a, b) -> a.length() - b.length();

// 複数行の本体
Function<String, String> upper = s -> {
    String result = s.trim();
    return result.toUpperCase();
};

例:Lambdaの使用

JAVA
import java.util.Arrays;
import java.util.List;
import java.util.function.*;

public class LambdaDemo {
    public static void main(String[] args) {
        // Predicate:条件をテスト
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(4));  // true
        System.out.println(isEven.test(7));  // false
        
        // Function:変換
        Function<String, Integer> length = s -> s.length();
        System.out.println(length.apply("Hello"));  // 5
        
        // Consumer:消費
        Consumer<String> print = s -> System.out.println("出力: " + s);
        print.accept("Java");  // 出力: Java
        
        // Supplier:提供
        Supplier<Double> random = () -> Math.random();
        System.out.println(random.get());
    }
}
▶ 試してみよう

Lambdaとコレクション

Lambdaにより、コレクション操作がより簡潔になります。

例:反復

JAVA
import java.util.Arrays;
import java.util.List;

public class LambdaCollection {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        
        // 従来の方法
        for (String name : names) {
            System.out.println(name);
        }
        
        // Lambda方法
        names.forEach(name -> System.out.println(name));
        
        // メソッド参照(より簡潔)
        names.forEach(System.out::println);
    }
}
▶ 試してみよう

例:ソート

JAVA
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class LambdaSort {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
        
        // 従来の方法
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return a.compareTo(b);
            }
        });
        
        // Lambda方法
        names.sort((a, b) -> a.compareTo(b));
        
        // メソッド参照
        names.sort(String::compareTo);
        
        System.out.println(names);  // [Alice, Bob, Charlie]
    }
}
▶ 試してみよう

メソッド参照

メソッド参照はLambda式の短縮形です。

4種類のメソッド参照

タイプ 構文
静的メソッド クラス名::静的メソッド Math::abs
インスタンスメソッド オブジェクト::インスタンスメソッド System.out::println
特定クラスのインスタンスメソッド クラス名::インスタンスメソッド String::length
コンストラクタ クラス名::new ArrayList::new

例:メソッド参照

JAVA
import java.util.Arrays;
import java.util.List;
import java.util.function.*;

public class MethodRefDemo {
    public static void main(String[] args) {
        // 静的メソッド参照
        Function<Integer, Integer> abs = Math::abs;
        System.out.println(abs.apply(-5));  // 5
        
        // インスタンスメソッド参照
        Consumer<String> print = System.out::println;
        print.accept("Hello");  // Hello
        
        // 特定クラスのインスタンスメソッド参照
        Function<String, Integer> length = String::length;
        System.out.println(length.apply("Hello"));  // 5
        
        // コンストラクタ参照
        Supplier<List<String>> listFactory = ArrayList::new;
        List<String> list = listFactory.get();
    }
}
▶ 試してみよう

包括的な例

JAVA
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LambdaPractice {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 偶数をフィルタリング
        List<Integer> evens = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
        System.out.println("偶数: " + evens);  // [2, 4, 6, 8, 10]
        
        // 二乗
        List<Integer> squares = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        System.out.println("二乗: " + squares);  // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
        
        // 合計
        int sum = numbers.stream()
            .reduce(0, (a, b) -> a + b);
        System.out.println("合計: " + sum);  // 55
        
        // 最大値
        int max = numbers.stream()
            .reduce(Integer::max)
            .orElse(0);
        System.out.println("最大値: " + max);  // 10
    }
}

❓ よくある質問

Q Lambdaと無名内部クラスの違いは何ですか?
A Lambdaはより簡潔ですが、関数型インターフェースでのみ機能します。無名内部クラスは任意のインターフェースまたはクラスで動作します。
Q いつLambdaを使用すべきですか?
A 関数型インターフェースを実装するとき、特にコレクション操作やイベント処理に使用します。
Q メソッド参照とLambdaのどちらを選ぶべきですか?
A Lambdaが既存のメソッドを呼び出すだけの場合、簡潔さのためにメソッド参照を使用してください。

📖 まとめ

📝 演習

  1. Lambda練習: Lambdaを使用して文字列を大文字に変換、長さを取得、空かどうかを確認
  2. コレクション操作: Lambdaを使用してコレクションをフィルタリング、変換、ソート
  3. カスタム関数型インターフェース: 数学演算インターフェースを定義し、Lambdaで加算/減算/乗算/除算を実装

次のレッスン

次のレッスンでは、列挙型と内部クラスを学びます — Javaの特殊なクラス。

100%