哪个规范支持可选参数?
在Java中,有几种模拟可选参数的方法:
方法重载。
void foo(String a, Integer b) { //... }
void foo(String a) { foo(a, 0); // 这里,0是b的默认值 }
foo("a", 2); foo("a");
这种方法的局限性之一是,如果您有两个相同类型的可选参数,并且其中任何一个可以省略,则此方法无法使用。
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");
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 bOpt) { Integer b = bOpt.isPresent() ? bOpt.get() : 0; //... }
foo("a", Optional.of(2)); foo("a", Optional.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"));
可变参数可以以某种方式实现该功能。除此之外,方法声明中的所有变量都必须提供。如果要使一个变量是可选的,可以使用不需要该参数的签名重载该方法。
private boolean defaultOptionalFlagValue = true;
public void doSomething(boolean optionalFlag) {
...
}
public void doSomething() {
doSomething(defaultOptionalFlagValue);
}
Java 5.0中有可选参数。只需像这样声明函数:
public void doSomething(boolean... optionalFlag) {
//default to "false"
//boolean flag = (optionalFlag.length >= 1) ? optionalFlag[0] : false;
}
现在你可以使用 doSomething();
或者 doSomething(true);
来调用函数。
doSomething() { doSomething(true); }
),没有数组需要处理,也没有歧义。 - rogerdpackpublic void addError(String path, String key, Object... params) {
}
params
变量是可选的。它被视为一个可为空的对象数组。
奇怪的是,在文档中找不到任何关于这个的信息,但它确实有效!
这是Java 1.5及以上版本的“新”功能(不支持Java 1.4或更早版本)。
我也看到用户bhoot在下面提到了这一点。
Java中没有可选参数。你可以重载函数并传递默认值来实现相同的效果。
void SomeMethod(int age, String name) {
//
}
// Overload
void SomeMethod(int age) {
SomeMethod(age, "John Doe");
}
之前提到了VarArgs和重载函数。另一个选项是使用Bloch Builder模式,代码会类似于这样:
MyObject my = new MyObjectBuilder().setParam1(value)
.setParam3(otherValue)
.setParam6(thirdValue)
.build();
尽管该模式最适合在构造函数中需要可选参数的情况下使用。在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);
}
}
}
if () {} else {}
对于代码维护来说不好。在我看来,方法重载才是正确的答案。 - Gwang-Jin Kim你可以使用方法重载来做这件事,就像这样。
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...){}
使用三个点:
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的回答)
(该句为注释,无需翻译)函数重载是可以的,但如果有很多需要默认值的变量,你最终会得到:
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提供的可变参数。
Optional
作为参数,但这篇回答非常好。以下是提供反对使用Optional
作为参数的另一种链接:此处。 - DherikUser
的类,其中包含大约15个字段。其中近10个字段是可选的,根据最终用户的输入可能为空或非空。与JS相比,创建User
对象需要编写大量额外的代码。对于这样的类,最佳解决方案是什么? - Amirhosein AlsomeMethod("test", 1)
和someMethod("test", 1, 2, 3)
。 - hohserg