Javaでオプションパラメータを使用するにはどうすればいいですか? 質問する

Javaでオプションパラメータを使用するにはどうすればいいですか? 質問する

オプションパラメータをサポートする仕様は何ですか?

ベストアンサー1

Java でオプション パラメータをシミュレートする方法はいくつかあります。

メソッドのオーバーロード

void foo(String a, Integer b) {
    //...
}

void foo(String a) {
    foo(a, 0); // here, 0 is a default value for b
}

foo("a", 2);
foo("a");

このアプローチの制限の 1 つは、同じタイプのオプション パラメーターが 2 つあり、そのいずれかを省略できる場合には機能しないことです。

ヴァラルグス

a) すべてのオプションパラメータは同じタイプです。

void foo(String a, Integer... b) {
    Integer b1 = b.length > 0 ? b[0] : 0;
    Integer b2 = b.length > 1 ? b[1] : 0;
    //...
}

foo("a");
foo("a", 1, 2);

b) オプションパラメータの種類は異なる場合があります。

void foo(String a, Object... b) {
    Integer b1 = 0;
    String b2 = "";
    if (b.length > 0) {
      if (!(b[0] instanceof Integer)) { 
          throw new IllegalArgumentException("...");
      }
      b1 = (Integer)b[0];
    }
    if (b.length > 1) {
        if (!(b[1] instanceof String)) { 
            throw new IllegalArgumentException("...");
        }
        b2 = (String)b[1];
        //...
    }
    //...
}

foo("a");
foo("a", 1);
foo("a", 1, "b2");

このアプローチの主な欠点は、オプションのパラメータが異なる型である場合、静的な型チェックが失われることです。さらに、各パラメータの意味が異なる場合は、それらを区別する方法が必要になります。

ヌル

これまでのアプローチの制限に対処するには、null 値を許可し、メソッド本体の各パラメータを分析します。

void foo(String a, Integer b, Integer c) {
    b = b != null ? b : 0;
    c = c != null ? c : 0;
    //...
}

foo("a", null, 2);

ここで、すべての引数値を指定する必要がありますが、デフォルト値は null になる場合があります。

オプションクラス

このアプローチは null に似ていますが、デフォルト値を持つパラメータに Java 8 Optional クラスを使用します。

void foo(String a, Optional<Integer> bOpt) {
    Integer b = bOpt.isPresent() ? bOpt.get() : 0;
    //...
}

foo("a", Optional.of(2));
foo("a", Optional.<Integer>absent());

Optional は呼び出し元に対してメソッド契約を明示的にしますが、そのようなシグネチャは冗長すぎると感じるかもしれません。

java.util.Optional更新: Java 8にはすぐに使用できるクラスが含まれているため、Java 8 ではこの特定の理由で guava を使用する必要はありません。ただし、メソッド名は少し異なります。

ビルダーパターン

ビルダー パターンはコンストラクターに使用され、別の Builder クラスを導入することによって実装されます。

class Foo {
    private final String a; 
    private final Integer b;

    Foo(String a, Integer b) {
      this.a = a;
      this.b = b;
    }

    //...
}

class FooBuilder {
  private String a = ""; 
  private Integer b = 0;
  
  FooBuilder setA(String a) {
    this.a = a;
    return this;
  }

  FooBuilder setB(Integer b) {
    this.b = b;
    return this;
  }

  Foo build() {
    return new Foo(a, b);
  }
}

Foo foo = new FooBuilder().setA("a").build();

地図

パラメータの数が多すぎて、ほとんどの場合にデフォルト値が使用される場合は、メソッド引数を名前/値のマップとして渡すことができます。

void foo(Map<String, Object> parameters) {
    String a = ""; 
    Integer b = 0;
    if (parameters.containsKey("a")) { 
        if (!(parameters.get("a") instanceof Integer)) { 
            throw new IllegalArgumentException("...");
        }
        a = (Integer)parameters.get("a");
    }
    if (parameters.containsKey("b")) { 
        //... 
    }
    //...
}

foo(ImmutableMap.<String, Object>of(
    "a", "a",
    "b", 2, 
    "d", "value")); 

Java 9 では、このアプローチはより簡単になりました。

@SuppressWarnings("unchecked")
static <T> T getParm(Map<String, Object> map, String key, T defaultValue) {
  return (map.containsKey(key)) ? (T) map.get(key) : defaultValue;
}

void foo(Map<String, Object> parameters) {
  String a = getParm(parameters, "a", "");
  int b = getParm(parameters, "b", 0);
  // d = ...
}

foo(Map.of("a","a",  "b",2,  "d","value"));

望ましい結果を得るために、これらのアプローチを組み合わせることができることに注意してください。

おすすめ記事