932

什么规范支持可选参数?

4

17 回答 17

1818

在 Java 中有几种方法可以模拟可选参数:

  1. 方法重载。

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

    void foo(String a) { foo(a, 0); // 这里,0 是 b 的默认值 }

    富(“一”,2);富(“一”);

这种方法的一个限制是,如果您有两个相同类型的可选参数并且其中任何一个都可以省略,则它不起作用。

  1. 可变参数。

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");

这种方法的主要缺点是,如果可选参数的类型不同,您将失去静态类型检查。此外,如果每个参数具有不同的含义,您需要一些方法来区分它们。

  1. 空值。为了解决以前方法的局限性,您可以允许空值,然后分析方法主体中的每个参数:

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

    foo("a", null, 2);

现在必须提供所有参数值,但默认值可能为空。

  1. 选修课。这种方法类似于空值,但对具有默认值的参数使用 Java 8 Optional 类:

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

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

    Optional 使调用者的方法契约明确,但是,人们可能会发现这样的签名过于冗长。

    更新:Java 8 包含java.util.Optional开箱即用的类,因此在 Java 8 中出于这个特殊原因不需要使用 guava。但是方法名称有点不同。

  2. 生成器模式。builder 模式用于构造函数,通过引入一个单独的 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();
    
  3. 地图。当参数的数量太大并且通常使用大多数默认值时,您可以将方法参数作为其名称/值的映射传递:

    void foo(Map<String, Object> 参数) { String a = ""; 整数 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"));
    

请注意,您可以结合使用这些方法中的任何一种来获得理想的结果。

于 2012-10-21T01:16:11.193 回答
579

可变参数可以做到这一点(在某种程度上)。除此之外,必须提供方法声明中的所有变量。如果您希望变量是可选的,您可以使用不需要参数的签名重载该方法。

private boolean defaultOptionalFlagValue = true;

public void doSomething(boolean optionalFlag) {
    ...
}

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}
于 2009-06-08T16:14:58.750 回答
134

Java 5.0 有可选参数。只需像这样声明您的函数:

public void doSomething(boolean... optionalFlag) {
    //default to "false"
    //boolean flag = (optionalFlag.length >= 1) ? optionalFlag[0] : false;
}

你可以打电话doSomething();doSomething(true);现在。

于 2010-04-02T12:02:24.640 回答
104

你可以使用这样的东西:

public void addError(String path, String key, Object... params) { 
}

params变量是可选的。它被视为可以为空的对象数组。

奇怪的是,我在文档中找不到任何关于此的内容,但它确实有效!

这是 Java 1.5 及更高版本中的“新”(Java 1.4 或更早版本不支持)。

我看到用户 bhoot 在下面也提到了这一点。

于 2010-12-03T21:41:36.033 回答
67

Java 中没有可选参数。您可以做的是重载函数,然后传递默认值。

void SomeMethod(int age, String name) {
    //
}

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}
于 2009-06-08T16:18:10.060 回答
23

已经提到了 VarArgs 和重载。另一种选择是 Bloch Builder 模式,它看起来像这样:

 MyObject my = new MyObjectBuilder().setParam1(value)
                                 .setParam3(otherValue)
                                 .setParam6(thirdValue)
                                 .build();

尽管该模式最适合您在构造函数中需要可选参数时。

于 2009-06-08T17:05:21.470 回答
16

在 JDK>1.5 中你可以这样使用它;

public class NewClass1 {

    public static void main(String[] args) {

        try {
            someMethod(18); // Age : 18
            someMethod(18, "John Doe"); // Age & Name : 18 & John Doe
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void someMethod(int age, String... names) {

        if (names.length > 0) {
            if (names[0] != null) {
                System.out.println("Age & Name : " + age + " & " + names[0]);
            }
        } else {
            System.out.println("Age : " + age);
        }
    }
}
于 2012-08-24T10:44:38.880 回答
8

精简版 :

使用 三个点

public void foo(Object... x) {
    String first    =  x.length > 0 ? (String)x[0]  : "Hello";
    int duration    =  x.length > 1 ? Integer.parseInt((String) x[1])     : 888;
}   
foo("Hii", ); 
foo("Hii", 146); 

(基于@VitaliiFedorenko 的回答)

于 2017-01-15T20:26:45.993 回答
8

你可以像这样使用方法重载来做事。

 public void load(String name){ }

 public void load(String name,int age){}

您也可以使用 @Nullable 注释

public void load(@Nullable String name,int age){}

只需将 null 作为第一个参数传递。

如果您传递相同类型的变量,您可以使用它

public void load(String name...){}
于 2016-08-19T11:33:36.543 回答
4

重载很好,但如果有很多变量需要默认值,你最终会得到:

public void methodA(A arg1) {  }    
public void methodA(B arg2) {  }
public void methodA(C arg3) {  }
public void methodA(A arg1, B arg2) {  }
public void methodA(A arg1, C arg3) {  }
public void methodA(B arg2, C arg3) {  }
public void methodA(A arg1, B arg2, C arg3) {  }

所以我建议使用Java提供的变量参数。

于 2016-08-19T11:20:25.293 回答
3

如果它是一个 API 端点,一个优雅的方法是使用“Spring”注解:

@GetMapping("/api/foos")
@ResponseBody
public String getFoos(@RequestParam(required = false, defaultValue = "hello") String id) { 
    return innerFunc(id);
}

请注意,在这种情况下,innerFunc 将需要该变量,并且由于它不是 api 端点,因此不能使用此 Spring 注释使其成为可选。参考:https ://www.baeldung.com/spring-request-param

于 2020-05-27T09:52:06.463 回答
2

您可以使用类似于构建器的类来包含这样的可选值。

public class Options {
    private String someString = "default value";
    private int someInt= 0;
    public Options setSomeString(String someString) {
        this.someString = someString;
        return this;
    }
    public Options setSomeInt(int someInt) {
        this.someInt = someInt;
        return this;
    }
}

public static void foo(Consumer<Options> consumer) {
    Options options = new Options();
    consumer.accept(options);
    System.out.println("someString = " + options.someString + ", someInt = " + options.someInt);
}

使用喜欢

foo(o -> o.setSomeString("something").setSomeInt(5));

输出是

someString = something, someInt = 5

要跳过您必须调用它的所有可选值,foo(o -> {});或者如果您愿意,您可以创建第二个foo()不采用可选参数的方法。

使用这种方法,您可以以任何顺序指定可选值,而不会产生任何歧义。与可变参数不同,您还可以拥有不同类的参数。如果您可以使用注释和代码生成来创建 Options 类,这种方法会更好。

于 2017-12-15T23:49:01.727 回答
1

这是一个老问题,甚至可能在引入实际 Optional 类型之前,但现在您可以考虑几件事: - 使用方法重载 - 使用具有避免在周围传递 NULL 的优点的 Optional 类型 在通常使用之前,Java 8 中引入了 Optional 类型来自第三方库,例如 Google 的 Guava。使用可选作为参数/参数可以被认为是过度使用,因为主要目的是将它用作返回时间。

参考:https ://itcodehub.blogspot.com/2019/06/using-optional-type-in​​-java.html

于 2019-06-08T14:59:54.923 回答
1

Java 现在在 1.8 中支持可选项,我一直坚持在 android 上编程,所以我使用空值,直到我可以重构代码以使用可选类型。

Object canBeNull() {
    if (blah) {
        return new Object();
    } else {
        return null;
    }
}

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}
于 2016-09-01T02:57:03.660 回答
0

Java 中不能使用默认参数。在 C#、C++ 和 Python 中,我们可以在哪里使用它们。

在 Java 中,我们必须使用 2 种方法(函数)而不是 1 种带有默认参数的方法。

例子:

Stash(int size); 

Stash(int size, int initQuantity);

http://parvindersingh.webs.com/apps/forums/topics/show/8856498-java-how-to-set-default-parameters-values-like-c-

于 2013-04-10T08:45:21.390 回答
0

我们可以通过方法重载或使用数据类型来制作可选参数...

|*| 方法重载:

RetDataType NameFnc(int NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(String NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(int NamePsgVar1, String NamePsgVar2)
{
    // |* Code Todo *|
    return RetVar;
}

最简单的方法是

|*| DataType... 可以是可选参数

RetDataType NameFnc(int NamePsgVar, String... stringOpnPsgVar)
{
    if(stringOpnPsgVar.length == 0)  stringOpnPsgVar = DefaultValue; 

    // |* Code Todo *|
    return RetVar;
}

于 2017-05-21T12:15:26.833 回答
0

如果您计划使用具有多个参数的接口,可以使用以下结构模式并实现或覆盖 apply - 一种基于您的要求的方法。

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}
于 2019-12-10T12:20:49.840 回答