0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

`fun interface`を使ってKotlinでもJava互換の関数型インターフェースを実現する

Posted at

関数型インターフェース

概要

ざっくり言うと、Javaで「関数を(型として)扱うためのインターフェース」が関数型インターフェースです。

関数を第一級オブジェクト(first-class object)として扱えないJavaで、第一級っぽく扱うための仕組みとしてJava 8で導入されました。そのため、「関数型インターフェース」という言葉は基本的にJavaの用語です1

定義

具体的には「 抽象メソッドを1つだけ持つインターフェース」と定義され、SAM(Single Abstract Method)インターフェースとも言います。

もう少し細かく言うと、主に下記のルールがあります。

  • 抽象メソッドは1つだけ
    • 2つ以上の抽象メソッドを定義するとコンパイルエラー
    • 非抽象メソッドなら複数あってもいい
    • 継承しているインターフェースの抽象メソッドも合算して1つだけ
  • プロパティや定数などは持っていい
Java
@FunctionalInterface // 関数型インターフェースの明示的宣言。抽象メソッドが2個以上あるとコンパイルエラーになる
public interface Logger {
    void log(String message);
}

Java公式の関数型インターフェース(functional interface)の説明は↓

Kotlinでの関数型インターフェース

Kotlin 1.4からはfun interfaceキーワードで関数型インターフェースを宣言できます。

Kotlin
fun interface Logger {
    fun log(message: String)
}

Kotlin 1.3までは、Javaの関数型インターフェースをKotlinから利用することはできましたが、Kotlin側で関数型インターフェースを作成することはできませんでした。

なお、fun interfaceはコンパイルすると@FunctionalInterface 付きの通常のインターフェースになるためJavaからも利用でき、JavaとKotlinで相互に運用できます。

何がうれしいの?

ラムダやメソッド参照でサクッと実装できる

関数型インターフェースは関数を扱うためのインターフェースであり、抽象メソッドを1つしか持てないと説明しました。これは言い換えれば、その単一メソッドさえ実装すれば完成するということです。
この単一メソッドの実装として、ラムダ式やメソッド参照を渡すことができます。するとSAM変換2という仕組みにより、匿名クラスのインスタンスが自動で生成されます。

// メソッド参照
val logger = Logger(::println)

このように、実装クラスを明示的に書くことなくラムダ式やメソッド参照で簡潔に実装できるのは関数型インターフェースのメリットの1つです。

またこれにより、テスト用のモックやスタブを用意するのも簡単になります。

名前をつけられるので意図が明確になる

単なる関数型((T) -> Rなど)では引数と戻り値の型しか分かりません。これに対し関数型インターフェースでは名前をつけることができます。
例えばConverter<T, R>であれば、TR変換する処理なのだという意図が自然に伝わり、可読性が向上します。

Kotlin
fun interface Converter<T, R> {
    fun convert(value: T): R
}

Java標準APIとの親和性

関数型インターフェースは、Javaの標準APIでも広く利用されています。
例えば以下のような場面では、関数型インターフェースが前提となっています。

ストリームAPI

mapfilterに渡す関数はFunction<T, R>Predicate<T>などの関数型インターフェースです。

Kotlin
users.stream()
    .filter { it.age >= 30 }  // Predicate<User>
    .map { it.name.uppercase() }  // Function<User, String>
    .toList()

スレッド処理

Runnablerun()メソッド1つだけの関数型インターフェースなので、Thread { ... }のように書けます。

Kotlin
Thread { fetchRemoteData() }.start()

ソートや比較

Comparator<T>compare(T, T)メソッド1つだけを持つため、ラムダで簡潔に書けます。

Kotlin
products.sortedWith { a, b -> a.price - b.price }  // Comparator<Product>
  1. 概念自体は他の言語にもありますが、関数型インターフェース(functional interface)というワード自体はJava(JVM)固有のものです

  2. SAM(抽象メソッドが1つだけのインターフェース)への代入や引数渡しのときに、ラムダ式・メソッド参照をそのインターフェース実装オブジェクトへ自動変換するコンパイラの仕組み

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?