Java のメソッドで複数の値を返す方法あれこれ (Generics, Record, Triple, Tuple, 標準ライブラリや外部ライブラリを使用)

## 概要

- Java のメソッドで複数の値を返す方法について、標準ライブラリや外部ライブラリを使用したサンプルコードを挙げる (3つの値を返すサンプルコード)

## 環境

- Java 15 (AdoptOpenJDK 15+36)
- Gradle 6.6.1
- macOS Catalina

## 使用している外部ライブラリ

- Kotlin Standard Library 1.4.10
- Scala Standard Library 2.13.3
- Apache Commons Lang 3.11
- Functional Java 4.8.1
- javatuples 1.2
- jOOλ 0.9.14
- Reactor Core 3.3.10
- Vavr 0.10.3

## 外部ライブラリを使わない方法

### 標準ライブラリ java.lang.Object[]

Object の配列に値をセットして返す。
受け取った側はキャストが必要。

```java
public class ArraySample {

  static Object[] getArray() {
    Object[] array = new Object[3];
    array[0] = "foo";
    array[1] = true;
    array[2] = 123456;
    return array;
  }

  public static void main(String[] args) {
    Object[] array = getArray();
    String  foo = (String) array[0];
    boolean bar = (boolean) array[1];
    int     baz = (int) array[2];
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

### 標準ライブラリ java.util.List

List の要素に値をセットして返す。
受け取った側はキャストが必要。

```java
import java.util.ArrayList;
import java.util.List;

public class ListSample {

  static List<Object> getList() {
    ArrayList<Object> list = new ArrayList<>();
    list.add("foo");
    list.add(true);
    list.add(123456);
    return list;
  }

  public static void main(String[] args) {
    List<Object> list = getList();
    String  foo = (String) list.get(0);
    boolean bar = (boolean) list.get(1);
    int     baz = (int) list.get(2);
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

### 標準ライブラリ java.util.Map

Map の要素に値をセットして返す。
受け取った側はキャストが必要。

```java
import java.util.HashMap;
import java.util.Map;

public class MapSample {

  static Map<String, Object> getMap() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("foo", "foo");
    map.put("bar", true);
    map.put("baz", 123456);
    return map;
  }

  public static void main(String[] args) {
    Map<String, Object> map = getMap();
    String  foo = (String) map.get("foo");
    boolean bar = (boolean) map.get("bar");
    int     baz = (int) map.get("baz");
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

### クラスを定義する

クラスを定義してフィールドに値をセットして返す。

```java
public class ClassSample {

  static class MyClass {
    String foo;
    boolean bar;
    int baz;
  }

  static MyClass getMyClass() {
    MyClass cls = new MyClass();
    cls.foo = "foo";
    cls.bar = true;
    cls.baz = 123456;
    return cls;
  }

  public static void main(String[] args) {
    MyClass cls = getMyClass();
    String  foo = cls.foo;
    boolean bar = cls.bar;
    int     baz = cls.baz;
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

### ジェネリクス化したクラスを定義する

ジェネリクス化したクラスを定義して、使用する際に型を指定する。
フィールドに値をセットして返す。

```java
public class GenericsSample {

  static class ValuesGenerics<T1, T2, T3> {
    T1 foo;
    T2 bar;
    T3 baz;
  }

  static ValuesGenerics<String, Boolean, Integer> getGenerics() {
    ValuesGenerics<String, Boolean, Integer> generics = new ValuesGenerics<>();
    generics.foo = "foo";
    generics.bar = true;
    generics.baz = 123456;
    return generics;
  }

  public static void main(String[] args) {
    ValuesGenerics<String, Boolean, Integer> generics = getGenerics();
    String  foo = generics.foo;
    boolean bar = generics.bar;
    int     baz = generics.baz;
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

### レコードを定義する

レコード (Java 16 から正式導入予定) を定義して使用する。

```java
public class RecordSample {

  record ValuesRecord(String foo, boolean bar, int baz) {
  }

  static ValuesRecord getRecord() {
    return new ValuesRecord("foo", true, 123456);
  }

  public static void main(String[] args) {
    ValuesRecord record = getRecord();
    String  foo = record.foo;
    boolean bar = record.bar;
    int     baz = record.baz;
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [JEP 384: Records \(Second Preview\)](https://openjdk.java.net/jeps/384)

## 外部ライブラリを使う方法

### Kotlin Standard Library (kotlin.Triple)

Kotlin の標準ライブラリを Java から使用する。
Kotlin の標準ライブラリには、2つの値を扱う Pair と、3つの値を扱う Triple がある。

```java
import kotlin.Triple;

public class KotlinSample {

  static Triple<String, Boolean, Integer> getTriple() {
    return new Triple<>("foo", true, 123456);
  }

  public static void main(String[] args) {
    Triple<String, Boolean, Integer> triple = getTriple();
    String  foo = triple.getFirst();
    boolean bar = triple.getSecond();
    int     baz = triple.getThird();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Triple \- Kotlin Programming Language](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/)

### Scala Standard Library (scala.Tuple3)

Scala の標準ライブラリを Java から使用する。
Scala の標準ライブラリには、1つの値を扱う Tuple1 から、22個の値を扱う Tuple22 まである。

```java
import scala.Tuple3;

public class ScalaSample {

  static Tuple3<String, Boolean, Integer> getTuple3() {
    return new Tuple3<>("foo", true, 123456);
  }

  public static void main(String[] args) {
    Tuple3<String, Boolean, Integer> tuple = getTuple3();
    String  foo = tuple._1();
    boolean bar = tuple._2();
    int     baz = tuple._3();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Scala Standard Library 2\.13\.3 \- scala\.Tuple3](https://www.scala-lang.org/api/2.13.3/scala/Tuple3.html)

### Apache Commons Lang (org.apache.commons.lang3.tuple.Triple)

Apache Commons Lang には、2つの値を扱う Pair と、3つの値を扱う Triple がある。

```java
import org.apache.commons.lang3.tuple.Triple;

public class ApacheCommonsLangSample {

  static Triple<String, Boolean, Integer> getTriple() {
    return Triple.of("foo", true, 123456);
  }

  public static void main(String[] args) {
    Triple<String, Boolean, Integer> triple = getTriple();
    String  foo = triple.getLeft();
    boolean bar = triple.getMiddle();
    int     baz = triple.getRight();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Triple \(Apache Commons Lang 3\.11 API\)](https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/tuple/Triple.html)

### Functional Java (fj.P3)

Functional Java には、1つの値を扱う P1 から、8個の値を扱う P8 まである。

```java
import fj.P;
import fj.P3;

public class FunctionalJavaSample {

  static P3<String, Boolean, Integer> getP3() {
    return P.p("foo", true, 123456);
  }

  public static void main(String[] args) {
    P3<String, Boolean, Integer> p = getP3();
    String  foo = p._1();
    boolean bar = p._2();
    int     baz = p._3();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [P3 \(core 4\.8\.1 API\)](http://www.functionaljava.org/javadoc/4.8.1/functionaljava/fj/P3.html)

### javatuples (org.javatuples.Triplet)

javatuples には、1つの値を扱う Unit から、10個の値を扱う Decade まである。

```java
import org.javatuples.Triplet;

public class JavatuplesSample {

  static Triplet<String, Boolean, Integer> getTriplet() {
    return Triplet.with("foo", true, 123456);
  }

  public static void main(String[] args) {
    Triplet<String, Boolean, Integer> triplet = getTriplet();
    String  foo = triplet.getValue0();
    boolean bar = triplet.getValue1();
    int     baz = triplet.getValue2();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Triplet \(javatuples 1\.2 API\)](https://www.javatuples.org/apidocs/org/javatuples/Triplet.html)

### jOOλ (org.jooq.lambda.tuple.Tuple3)

jOOλ には、0個の値を扱う Tuple0 から、16個の値を扱う Tuple16 まである。

```java
import org.jooq.lambda.tuple.Tuple;
import org.jooq.lambda.tuple.Tuple3;

public class JoolSample {

  static Tuple3<String, Boolean, Integer> getTuple3() {
    return Tuple.tuple("foo", true, 123456);
  }

  public static void main(String[] args) {
    Tuple3<String, Boolean, Integer> tuple = getTuple3();
    String  foo = tuple.v1();
    boolean bar = tuple.v2();
    int     baz = tuple.v3();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Tuple3 \(jOOL 0\.9\.14 API\)](https://www.jooq.org/products/jOO%ce%bb/javadoc/0.9.14/org/jooq/lambda/tuple/Tuple3.html)

### Reactor (reactor.util.function.Tuple3)

Reactor には、2個の値を扱う Tuple2 から、8個の値を扱う Tuple8 まである。

```java
import reactor.util.function.Tuple3;
import reactor.util.function.Tuples;

public class ReactorSample {

  static Tuple3<String, Boolean, Integer> getTuple3() {
    return Tuples.of("foo", true, 123456);
  }

  public static void main(String[] args) {
    Tuple3<String, Boolean, Integer> tuple = getTuple3();
    String  foo = tuple.getT1();
    boolean bar = tuple.getT2();
    int     baz = tuple.getT3();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Tuple3 \(reactor\-core 3\.3\.10\.RELEASE\)](https://projectreactor.io/docs/core/3.3.10.RELEASE/api/reactor/util/function/Tuple3.html)

### Vavr (io.vavr.Tuple3)

Vavr には、0個の値を扱う Tuple0 から、8個の値を扱う Tuple8 まである。

```java
import io.vavr.Tuple;
import io.vavr.Tuple3;

public class VavrSample {

  static Tuple3<String, Boolean, Integer> getTuple3() {
    return Tuple.of("foo", true, 123456);
  }

  public static void main(String[] args) {
    Tuple3<String, Boolean, Integer> tuple = getTuple3();
    String  foo = tuple._1();
    boolean bar = tuple._2();
    int     baz = tuple._3();
    System.out.println(foo);
    System.out.println(bar);
    System.out.println(baz);
  }
}
```

参考: [Tuple3 \(Vavr 0\.10\.3 API\)](https://www.javadoc.io/doc/io.vavr/vavr/0.10.3/io/vavr/Tuple3.html)

## サンプルコード実行用ファイル

### build.gradle

```groovy
plugins {
  id 'java'
  id 'application'
}

repositories {
  mavenCentral()
}

dependencies {
  // 必要なライブラリ群
  implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.4.10'
  implementation 'org.scala-lang:scala-library:2.13.3'
  implementation 'org.apache.commons:commons-lang3:3.11'
  implementation 'org.functionaljava:functionaljava:4.8.1'
  implementation 'org.javatuples:javatuples:1.2'
  implementation 'org.jooq:jool:0.9.14'
  implementation 'io.projectreactor:reactor-core:3.3.10.RELEASE'
  implementation 'io.vavr:vavr:0.10.3'
}

sourceCompatibility = JavaVersion.VERSION_15
mainClassName = "App"

tasks.withType(JavaCompile) {
  // Java 15 のプレビュー機能を使う
  options.compilerArgs += ['--enable-preview']
}

application {
  // Java 15 のプレビュー機能を使う
  applicationDefaultJvmArgs = ['--enable-preview']
}
```

### サンプルコードまとめて実行用クラス

```java
public class App {

  public static void main(String[] args) {
    ArraySample.main(args);
    ListSample.main(args);
    MapSample.main(args);
    ClassSample.main(args);
    GenericsSample.main(args);
    RecordSample.main(args);
    KotlinSample.main(args);
    ScalaSample.main(args);
    ApacheCommonsLangSample.main(args);
    FunctionalJavaSample.main(args);
    JavatuplesSample.main(args);
    JoolSample.main(args);
    ReactorSample.main(args);
    VavrSample.main(args);
  }
}
```

## 参考資料

- [JavaでTupleが欲しくなったときメモ \- Qiita](https://qiita.com/disc99/items/738fb328b22c05900b8a)