JavaのOptionalをgolangにportする

# はじめに

Java さわってますか？私は10年近くさわってません。[JDK 8 Release Notes](https://www.oracle.com/java/technologies/javase/8all-relnotes.html#JSERN108)を見ると2014年5月15日にリリースされているので大体10年です。

この10年位は golang をさわってます。

Javaの勉強もしなければなと思いつつも、作りたいアプリケーションも特にありません。ドキュメントを読んでるだけというのも長続きしないかなと思ってます。そんな時に以下の2つのブログを読みました。

* [Goのヌル安全について](https://zenn.dev/nobonobo/articles/f49dd93073fdd3c6c663)
* [元JavaエンジニアがGoに感じた「表現力の低さ」と「開発生産性」の話](https://inside.dmm.com/articles/ex-java-engineer-thoughts-about-go/)

これらを読んで「 Java の Optional を golang に移植したら、 Java のコードも読むし勉強になるかな？」と思いました。そんなわけでゆるゆると Java の Optional を golang に移植していこうと思います。その際に得た知見をゆるゆるとブログに書いていきます。

Java の Optional の書き方はあまりgolang的なものではないなと思ってます。なので、「これがあれば幸せになれるよ」的なものを作ろうとしている訳ではありません。あくまでネタとして、ゆるゆる見て頂ければと思います。

# 移植対象

移植対象の Optional はJDK 21 にしました。具体的には以下のソースコードです。

* https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/java/util/Optional.java

また JavaDoc は以下のものになります。

* https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/Optional.html

また Optional ではいかの interface が利用されています。

* [Predicate](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/function/Predicate.html)
* [Function](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/function/Function.html)
* [Consumer](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/function/Consumer.html)
* [Supplier](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/function/Supplier.html)
* [Runnable](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/lang/Runnable.html)

これらも適宜実装していきます。前振りが長くなりましたが、ここから本編です。

# Functionの実装

Optional の移植といいつつ、まずは Function を実装します。 Optional のメソッドを実装するために必要なため、 Function から始めます。Javaのソースコードは[こちら](https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/java/util/function/Function.java)です。 以下にコメントを削除したコードを記載します。

```java
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
```

このコードでgolangでは直接対応出来無い部分は以下の3つになります。

1. メソッドに対するgenericの適用
2. default method の実装
3. static method の実装

この中で特に問題になるのが 1 です。例えば以下の interface を golang で定義したとします。
	
```go
type Function[T any, R any, V any] interface {
	Apply(T) R
	AddThen(Function[R, V, T]) Function[T, V, R]
    Compose(Function[V, T, R]) Function[V, R, T]
}
```

形は Java の `Function` と似ていますが、実際は全く異るものです。

Java の場合は `compose` と `addThen` というメソッドに対して `V` という型パラメータが指定されています。このため、この `V` は `compose` や `addThen` が利用される時に決定されます。複数の個所で異る `V` と使っても問題有ません。ある場所では `Integer` でも、別の場所では `String` を利用できます。勿論、 `compose` と `addThen` では別の `V` を利用できます。

これに対して golang では `V` は `Function` interface に対して指定されています。このため、 `Function` interface を実装する構造体を実装する際に決定されなければなりません。 Java とは異り、 `Compose` と `AddThen` は同じ `V` を使わなければなりません。また `Compose` と `AddThen` は使われる全ての場所で同じ `V` を使わなければなりません。

これは、 golang がコンパイル時に型パラメータと対応した構造体やinterfaceを生成しているのに対し、 Java はコンパイル時に型のチェックを行っているだけという違いから生れるものだと思います。 golang では設定されたそれぞれの型パラメータに対して対応する実体が生成されます。例えば [slices.Concat](https://pkg.go.dev/slices@go1.22.2#Concat) に対して 

1. `slices.Concat([]int{1, 2}, []int{3})`
2. `slices.Concat([]string{"foo", "bar"}, []string{"buz"})` 

は別の関数が生成されます。それにたいして Java の [ArrayList](https://docs.oracle.com/javase/jp/21/docs/api/java.base/java/util/ArrayList.html)では、 

1. `new ArrayList<Integer>()`
2. `new ArrayList<String>()`

をした場合、別のクラスは生成されません。あくまで `ArrayList<Object>` が存在するだけです。それぞれは利用されている場所で `Integer` や `String` のみを受け付けるようコンパイル時にチェックされるだけです。

実際にバイトコードではこの情報は削除されます。この削除を[Type erasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html)と呼びます。この結果、 Java ではメソッドに型パラメータが指定されていたとしても、利用されないならば、無視してコンパイルできることになります。 `andThen` が使われない場合は、`andThen` の戻値の `Function<T, R>` の `R` 部分は存在しなくてもいいわけです。しかし golang の場合は、利用しないとしてもその型パラメータの指定は必要になります。それがなければ interface を生成できないからです。

長々書きましたが、結論としては `golangだと andThen や compose 相当のメソッドは定義できないので、関数にする` という結論になりました。

1.の `メソッドに対するgenericの適用` だけでかなりながくなりまし。 `2. default method の実装` については今回はdefault methodを使わなくて済んだので、今後必要となった時に考えます。`3. static method の実装` については関数で対応します。

上記をふまえて実装した `Java` の `Function` に対応する機能が以下のものになります。

* [applier.go](https://github.com/dairyo/j2g/blob/5d580229751bd171b8e5c42d283534b343d29f98/java/util/function/applier.go)

Functionとの相違点をまとめると

1. interface名は `Applier` にした
   * 残ったメソッドが `apply` のみだったため、golangの慣習に従った
2. Java のメソッド `andThen` と `compose` は `Compose` 関数に纏めた
   * メソッドだから前に付けるか後ろに付けるかが問題になる。関数ならば問題にならない
3. satic method の `identity` も関数の `Identity` にした
4. `NewApplierFunc` と `NewApplierFuncWithNoError` を導入し、関数を簡単に `Applier` interface へ適合できるようにした
   * Javaの場合は [FunctionalInterface](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/FunctionalInterface.html) を満していればメソッドを簡単に `Function` interfaceに代入できるが、 `golang` ではそれができないため

# まとめ
`Function` の移植だけでもかなりの調べることがありました。色々調べているつもりでしたが、全然足りていないと実感しました。公式ドキュメントをちゃんと調べるいい機会でした。

今後は `Predicate` などを移植していきます。Javaとgolangの仕様を調べないといけないので、楽しみです。

# 参考文献
* [Java のInterface methodの定義](https://go.dev/ref/spec#Function_types)
* [golang interfaceの定義](https://go.dev/ref/spec#Interface_types)

# つづき
[JavaのOptionalをgolangにportする - Method Reference と Method values](https://qiita.com/shinnara/items/9f1a0c45b859e8ff0211)