关于方法:Java是否支持默认参数值?

Does Java support default parameter values?

我遇到了一些Java代码,它们具有以下结构:

1
2
3
4
5
6
7
8
9
public MyParameterizedFunction(String param1, int param2)
{
    this(param1, param2, false);
}

public MyParameterizedFunction(String param1, int param2, boolean param3)
{
    //use all three parameters here
}

我知道,在C++中,我可以给一个参数赋值默认值。例如:

1
void MyParameterizedFunction(String param1, int param2, bool param3=false);

Java支持这种语法吗?这两步语法更可取有什么原因吗?


不,您发现的结构是Java如何处理它(即,重载而不是默认参数)。

对于构造函数,请参见有效Java:编程语言指南的项目1技巧(考虑静态工厂方法而不是构造函数),如果重载变得越来越复杂。对于其他方法,重命名某些情况或使用参数对象可能会有所帮助。这是当你有足够的复杂性,区分是困难的。一个明确的情况是你必须用参数的顺序来区分,而不仅仅是数字和类型。


不,但是您可以使用构建器模式,如本堆栈溢出答案中所述。

如链接答案中所述,构建器模式允许您编写类似

1
2
3
4
5
6
Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

其中一些字段可以有默认值,或者是可选的。


在Java中有几种模拟默认参数的方法:

  • 方法重载。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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");

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

  • 瓦拉格斯

    a)所有可选参数类型相同:

    1
    2
    3
    4
    5
    6
    7
    8
    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)可选参数的类型可能不同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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
    2
    3
    4
    5
    6
    7
    void foo(String a, Integer b, Integer c) {
        b = b != null ? b : 0;
        c = c != null ? c : 0;
        //...
    }

    foo("a", null, 2);

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

  • 可选类。此方法类似于空值,但对于具有默认值的参数,使用Java 8可选类:

    1
    2
    3
    4
    5
    6
    7
    void foo(String a, Optional<Integer> bOpt) {
        Integer b = bOpt.isPresent() ? bOpt.get() : 0;
        //...
    }

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

    可选将方法约定显式地用于调用方,但是,可能会发现这样的签名太冗长。

  • 构建器模式。生成器模式用于构造函数,通过引入单独的生成器类来实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     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();
  • 地图。当参数的数目太大,并且对大多数参数而言,通常使用默认值时,可以将方法参数作为其名称/值的映射传递:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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 = (String)parameters.get("a");
        } else if (parameters.containsKey("b")) {
            //...
        }
        //...
    }

    foo(ImmutableMap.<String, Object>of(
       "a","a",
       "b", 2,
       "d","value"));
  • 请注意,您可以结合这些方法中的任何一种,以获得理想的结果。


    遗憾的是,没有。


    不幸的是,是的。

    1
    void MyParameterizedFunction(String param1, int param2, bool param3=false) {}

    可以用Java 1.5编写为:

    1
    2
    3
    4
    void MyParameterizedFunction(String param1, int param2, Boolean... params) {
        assert params.length <= 1;
        bool param3 = params.length > 0 ? params[0].booleanValue() : false;
    }

    但是,是否应该取决于您对生成

    1
    new Boolean[]{}

    对于每个呼叫。

    对于多个默认参数:

    1
    void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}

    可以用Java 1.5编写为:

    1
    2
    3
    4
    5
    6
    7
    8
    void MyParameterizedFunction(String param1, int param2, Object... p) {
        int l = p.length;
        assert l <= 2;
        assert l < 1 || Boolean.class.isInstance(p[0]);
        assert l < 2 || Integer.class.isInstance(p[1]);
        bool param3 = l > 0 && p[0] != null ? ((Boolean)p[0]).booleanValue() : false;
        int param4 = l > 1 && p[1] != null ? ((Integer)p[1]).intValue() : 42;
    }

    这与C++语法匹配,它只允许在参数列表末尾出现缺省参数。

    除了语法之外,在运行时,对运行的默认参数进行时间类型检查和C++类型检查它们之间存在差异。


    不,但是你可以很容易地模仿它们。C++中的内容是:

    1
    public: void myFunction(int a, int b=5, string c="test") { ... }

    在Java中,它将是一个重载函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void myFunction(int a, int b, string c) { ... }

    public void myFunction(int a, int b) {
        myFunction(a, b,"test");
    }

    public void myFunction(int a) {
        myFunction(a, 5);
    }

    前面提到,默认参数导致函数重载中出现不明确的情况。这是不正确的,我们可以看到,在C++的情况下:是的,也许它可以创建模棱两可的情况,但是这些问题可以很容易地处理。它不只是在Java中开发,可能是因为创建者想要一个更简单的语言,因为C++是正确的,这是另一个问题。但我们大多数人认为他不使用Java,因为它很简单。


    你可以在Scala中运行,它运行在JVM上,与Java程序兼容。http://www.scala-lang.org网站/

    1
    class Foo(var prime: Boolean = false, val rib: String)  {}


    我可能在这里说明了明显的问题,但是为什么不自己简单地实现"默认"参数呢?

    1
    2
    3
    4
    5
    6
    7
    8
    public class Foo() {
            public void func(String s){
                    func(s, true);
            }
            public void func(String s, boolean b){
                    //your code here
            }
    }

    默认情况下,您将使用

    func("我的字符串");

    如果你不想使用默认值,你可以使用

    func("我的字符串",false);


    不,但最简单的实现方法是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public myParameterizedFunction(String param1, int param2, Boolean param3) {

        param3 = param3 == null ? false : param3;
    }

    public myParameterizedFunction(String param1, int param2) {

        this(param1, param2, false);
    }

    或者,如果满足以下条件,则可以使用三元运算符而不是三元运算符:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public myParameterizedFunction(String param1, int param2, Boolean param3) {

        if (param3 == null) {
            param3 = false;
        }
    }

    public myParameterizedFunction(String param1, int param2) {

        this(param1, param2, false);
    }


    不,一般来说,Java没有很多语法糖,因为他们试图制造一种简单的语言。


    不。

    通过传递具有智能默认值的对象,可以实现相同的行为。但这又取决于你手头的情况。


    正如斯卡拉提到的,柯特林也值得一提。在Kotlin函数中,参数也可以有默认值,甚至可以引用其他参数:

    1
    2
    3
    fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
        ...
    }

    与斯卡拉一样,Kotlin运行在JVM上,并且可以很容易地集成到现有Java项目中。


    不支持它,但有几个选项,如使用带有语法糖的参数对象模式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Foo() {
        private static class ParameterObject {
            int param1 = 1;
            String param2 ="";
        }

        public static void main(String[] args) {
            new Foo().myMethod(new ParameterObject() {{ param1 = 10; param2 ="bar";}});
        }

        private void myMethod(ParameterObject po) {
        }
    }

    在这个示例中,我们用默认值构造ParameterObject,并在类实例初始化部分{ param1 = 10; param2 ="bar";}中重写它们。


    尝试此解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public int getScore(int score, Integer... bonus)
    {
        if(bonus.length > 0)
        {
            return score + bonus[0];
        }

        return score;
    }


    可以使用Java方法调用生成器来自动生成具有默认值的生成器。

    只需将@GenerateMethodInvocationBuilder添加到类或接口,并将@Default添加到需要默认值的方法中的参数。编译器将在编译时使用您在注释中指定的默认值生成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @GenerateMethodInvocationBuilder
    public class CarService {
     public CarService() {
     }

     public String getCarsByFilter(//
       @Default("Color.BLUE") Color color, //
       @Default("new ProductionYear(2001)") ProductionYear productionYear,//
       @Default("Tomas") String owner//
     ) {
      return"Filtering..." + color + productionYear + owner;
     }
    }

    然后您可以调用这些方法。

    1
    2
    3
    CarService instance = new CarService();
    String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
      .invoke(instance);

    或者将任何默认值设置为其他值。

    1
    2
    3
    4
    CarService instance = new CarService();
    String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
      .withColor(Color.YELLOW)//
      .invoke(instance);

    在Java 8中工作的HTTPS://StAccOfFult.COM/A/13864 910/2423 964的类似方法是使用具有默认吸气剂的接口。这将是更详细的空白,但是可模拟的,当您有一堆实例,您实际上想引起对参数的注意时,这非常好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Foo() {
        public interface Parameters {
            String getRequired();
            default int getOptionalInt(){ return 23; }
            default String getOptionalString(){ return"Skidoo"; }
        }

        public Foo(Parameters parameters){
            //...
        }

        public static void baz() {
            final Foo foo = new Foo(new Person() {
                @Override public String getRequired(){ return"blahblahblah"; }
                @Override public int getOptionalInt(){ return 43; }
            });
        }
    }

    我就是这样做的…这可能不如对定义的参数使用"可选参数"那么方便,但它可以完成以下工作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public void postUserMessage(String s,boolean wipeClean)
    {
        if(wipeClean)
        {
            userInformation.setText(s +"
    "
    );
        }
        else
        {
            postUserMessage(s);
        }
    }

    public void postUserMessage(String s)
    {
        userInformation.appendText(s +"
    "
    );
    }

    注意,我可以只用一个字符串来调用相同的方法名,也可以用一个字符串和一个布尔值来调用它。在这种情况下,将wipeclean设置为true将用提供的字符串替换我的文本区域中的所有文本。将wipeclean设置为false或将其全部忽略,只需将提供的文本附加到文本区域。

    另外请注意,我并没有在这两个方法中重复代码,我只是添加了一个功能,即通过创建一个新方法来重置文本区域,只使用添加的布尔值使用相同的名称。

    实际上,我认为这比Java为我们的参数提供了一个"可选参数"稍微干净一些,因为我们需要对默认值进行编码等。在这个例子中,我不需要担心任何这些。是的,我已经在课堂上增加了另一种方法,但从长远来看,以我的拙见来看,它更容易阅读。


    有六个或更多的问题,比如这个,最终你会得到静态的工厂模式…请参阅CryptoAPI。排序很难解释,但请这样想:如果您有一个构造函数、默认值或其他值,那么传播大括号之外的状态的唯一方法是使用布尔值是有效的;(与空值一起作为默认值v failed constructor),或者抛出一个异常,该异常在从字段用户处获取时永远不会提供信息。

    代码是正确的,该死的,我写了一千行的构造器,并做了我需要的。我发现在对象构造中使用是有效的——换句话说,是两行构造函数——但是出于某种原因,我正在迁移到静态工厂模式。我只是觉得如果在方法调用中,您可以做很多事情,仍然存在sync()问题,但是默认值可以更好地"替换"(更安全)

    我认为我们需要在这里解决的问题是,将空值作为默认值vis-a-vis something string one=new string(");作为成员变量,然后在将字符串分配给构造函数之前检查空值。

    非常了不起的原始,平流层计算机科学在爪哇做的数量。

    C++等有供应商LIBS,是的。Java可以在大型服务器上胜过它们,因为它是巨大的工具箱。学习静态初始值设定项块,留下来。


    现在我花了相当长的时间来研究如何将此方法与返回值的方法一起使用,到目前为止我还没有看到任何示例,我认为在这里添加此方法可能很有用:

    1
    2
    3
    4
    5
    6
    7
    8
    int foo(int a) {
        // do something with a
        return a;
    }

    int foo() {
        return foo(0); // here, 0 is a default value for a
    }


    不,但是我们有函数重载形式的替代方法。

    未传递参数时调用

    1
    2
    3
    4
    5
    6
    void operation(){

    int a = 0;
    int b = 0;

    }

    当传递"a"参数时调用

    1
    2
    3
    4
    5
    6
    void operation(int a){

    int b = 0;
    //code

    }

    当参数b通过时调用

    1
    2
    3
    void operation(int a , int b){
    //code
    }