如何在 Java 中使用可选参数?

发布于 2024-07-23 08:17:30 字数 18 浏览 7 评论 0原文

什么规格支持可选参数?

What specification supports optional parameters?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

耳钉梦 2024-07-30 08:17:30

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

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

Varargs

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

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

现在

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

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

foo("a", null, 2);

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

可选类

此方法与 null 类似,但对具有默认值的参数使用 Java 8 可选类:

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 8 包含开箱即用的 java.util.Optional 类,因此在 Java 8 中无需为此特定原因使用 guava。方法名称有点不同尽管。

Builder 模式

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

Maps

当参数数量过多并且通常使用大多数默认值时,可以将方法参数作为其名称的映射传递/values:

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

请注意,您可以组合这些方法中的任何一种来实现理想的结果。

There are several ways to simulate optional parameters in Java.

Method overloading

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

One of the limitations of this approach is that it doesn't work if you have two optional parameters of the same type and any of them can be omitted.

Varargs

a) All optional parameters are of the same type:

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) Types of optional parameters may be different:

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

The main drawback of this approach is that if optional parameters are of different types you lose static type checking. Furthermore, if each parameter has the different meaning you need some way to distinguish them.

Nulls

To address the limitations of the previous approaches you can allow null values and then analyze each parameter in a method body:

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

foo("a", null, 2);

Now all arguments values must be provided, but the default ones may be null.

Optional class

This approach is similar to nulls, but uses Java 8 Optional class for parameters that have a default value:

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 makes a method contract explicit for a caller, however, one may find such signature too verbose.

Update: Java 8 includes the class java.util.Optional out-of-the-box, so there is no need to use guava for this particular reason in Java 8. The method name is a bit different though.

Builder pattern

The builder pattern is used for constructors and is implemented by introducing a separate Builder class:

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

Maps

When the number of parameters is too large and for most of the default values are usually used, you can pass method arguments as a map of their names/values:

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

In Java 9, this approach became easier:

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

Please note that you can combine any of these approaches to achieve a desirable result.

迷路的信 2024-07-30 08:17:30

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

private boolean defaultOptionalFlagValue = true;

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

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}

varargs could do that (in a way). Other than that, all variables in the declaration of the method must be supplied. If you want a variable to be optional, you can overload the method using a signature which doesn't require the parameter.

private boolean defaultOptionalFlagValue = true;

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

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}
-小熊_ 2024-07-30 08:17:30

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

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

您现在可以使用 doSomething();doSomething(true); 进行调用。

There is optional parameters with Java 5.0. Just declare your function like this:

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

you could call with doSomething(); or doSomething(true); now.

寻找我们的幸福 2024-07-30 08:17:30

您可以使用如下内容:

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

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

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

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

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

You can use something like this:

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

The params variable is optional. It is treated as a nullable array of Objects.

Strangely, I couldn't find anything about this in the documentation, but it works!

This is "new" in Java 1.5 and beyond (not supported in Java 1.4 or earlier).

I see user bhoot mentioned this too below.

时光暖心i 2024-07-30 08:17:30

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

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

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}

There are no optional parameters in Java. What you can do is overloading the functions and then passing default values.

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

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}
暗地喜欢 2024-07-30 08:17:30

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

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

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

VarArgs and overloading have been mentioned. Another option is a Bloch Builder pattern, which would look something like this:

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

Although that pattern would be most appropriate for when you need optional parameters in a constructor.

初相遇 2024-07-30 08:17:30

在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);
        }
    }
}

In JDK>1.5 you can use it like this;

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);
        }
    }
}
大海や 2024-07-30 08:17:30

您可以使用方法重载来完成这样的事情。

 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...){}

You can do thing using method overloading like this.

 public void load(String name){ }

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

Also you can use @Nullable annotation

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

simply pass null as first parameter.

If you are passing same type variable you can use this

public void load(String name...){}
灼疼热情 2024-07-30 08:17:30

简短版本:

使用三个点

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的回答)

Short version :

Using three dots:

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

(based on @VitaliiFedorenko's answer)

梨涡 2024-07-30 08:17:30

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

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 提供的变量参数

Overloading is fine, but if there's a lot of variables that needs default value, you will end up with :

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) {  }

So I would suggest use the Variable Argument provided by Java.

殤城〤 2024-07-30 08:17:30

您可以使用一个与构建器非常相似的类来包含您的可选值,如下所示。

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 类,那么这种方法会更好。

You can use a class that works much like a builder to contain your optional values like this.

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

Use like

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

Output is

someString = something, someInt = 5

To skip all the optional values you'd have to call it like foo(o -> {}); or if you prefer, you can create a second foo() method that doesn't take the optional parameters.

Using this approach, you can specify optional values in any order without any ambiguity. You can also have parameters of different classes unlike with varargs. This approach would be even better if you can use annotations and code generation to create the Options class.

徒留西风 2024-07-30 08:17:30

如果它是 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

If it's an API endpoint, an elegant way is to use "Spring" annotations:

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

Notice in this case that the innerFunc will require the variable, and since it's not api endpoint, can't use this Spring annotation to make it optional.
Reference: https://www.baeldung.com/spring-request-param

感性 2024-07-30 08:17:30

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

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

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}

Java now supports optionals in 1.8, I'm stuck with programming on android so I'm using nulls until I can refactor the code to use optional types.

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

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}
蛮可爱 2024-07-30 08:17:30

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

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

This is an old question maybe even before actual Optional type was introduced but these days you can consider few things:
- use method overloading
- use Optional type which has advantage of avoiding passing NULLs around
Optional type was introduced in Java 8 before it was usually used from third party lib such as Google's Guava. Using optional as parameters / arguments can be consider as over-usage as the main purpose was to use it as a return time.

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

七颜 2024-07-30 08:17:30

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

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

示例:

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-

Default arguments can not be used in Java. Where in C#, C++ and Python, we can use them..

In Java, we must have to use 2 methods (functions) instead of one with default parameters.

Example:

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-

深海夜未眠 2024-07-30 08:17:30

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

|*| 方法重载:

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;
}

We can make optional parameter by Method overloading or Using DataType...

|*| Method overloading :

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;
}

Easiest way is

|*| DataType... can be optional parameter

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

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

天暗了我发光 2024-07-30 08:17:30

如果您计划使用具有多个参数的界面
人们可以使用以下结构模式并实现或重写 apply - 一种基于您的要求的方法。

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}

If you are planning to use an interface with multiple parameters,
one can use the following structural pattern and implement or override apply - a method based on your requirement.

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文