Java optional parameters

2019-01-01 04:15发布

问题:

How do I use optional parameters in Java? What specification supports optional parameters?

回答1:

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


回答2:

There are several ways to simulate optional parameters in Java:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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();
    
  6. 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.



回答3:

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.



回答4:

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.



回答5:

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


回答6:

Unfortunately Java doesn\'t support default parameters directly.

However, I\'ve written a set of JavaBean annotations, and one of them support default parameters like the following:

protected void process(
        Processor processor,
        String item,
        @Default(\"Processor.Size.LARGE\") Size size,
        @Default(\"red\") String color,
        @Default(\"1\") int quantity) {
    processor.process(item, size, color, quantity);
}
public void report(@Default(\"Hello\") String message) {
    System.out.println(\"Message: \" + message);
}

The annotation processor generates the method overloads to properly support this.

See http://code.google.com/p/javadude/wiki/Annotations

Full example at http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample



回答7:

VarArgs and overloading have been mentioned. Another option is a 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.



回答8:

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


回答9:

It would depends on what you want to achieve, varargs or method overloading should solve most scenarios.

but keep in mind not to over use method overloading. it brings confusion.



回答10:

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)



回答11:

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


回答12:

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. Here\'s a link for explanation.



回答13:

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
}


回答14:

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.



回答15:

Default arguments can not be used in Java and C#. Where in 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-



回答16:

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

|*| Method overloading :

RetDtaTyp NamFnc(String NamPsgVar)
{
    // |* CodTdo *|
    return RetVar;
}

RetDtaTyp NamFnc(String NamPsgVar)
{
    // |* CodTdo *|
    return RetVar;
}

RetDtaTyp NamFnc(int NamPsgVar1, String NamPsgVar2)
{
    // |* CodTdo *|
    return RetVar;
}

Easiest way is

|*| DataType... can be optional parameter

RetDtaTyp NamFnc(int NamPsgVar, String... SrgOpnPsgVar)
{
    if(SrgOpnPsgVar.length == 0)  SrgOpnPsgVar = DefSrgVar; 

    // |* CodTdo *|
    return RetVar;
}