Java是否支持默认参数值?

gna*_*avi 1569 java parameters methods overloading default-parameters

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

public MyParameterizedFunction(String param1, int param2)
{
    this(param1, param2, false);
}

public MyParameterizedFunction(String param1, int param2, boolean param3)
{
    //use all three parameters here
}
Run Code Online (Sandbox Code Playgroud)

我知道在C++中我可以为参数指定一个默认值.例如:

void MyParameterizedFunction(String param1, int param2, bool param3=false);
Run Code Online (Sandbox Code Playgroud)

Java是否支持这种语法?有没有理由说这两步语法更可取?

Kat*_*one 896

不,您找到的结构是Java如何处理它(即,使用重载而不是默认参数).

对于构造函数,如果重载变得复杂,请参阅Effective Java:Programming Language Guide的第1项提示(考虑静态工厂方法而不是构造函数).对于其他方法,重命名某些情况或使用参数对象可能会有所帮助.这是因为当你有足够的复杂性时难以区分.一个明确的情况是你必须使用参数的顺序区分,而不仅仅是数字和类型.

  • @JarrodRoberson:静态工厂方法不比"新"更有害.它们被[使用](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Paths.html#get-java.lang.String-java.lang.String ... - )新代码中的所有[时间](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#of-int-int-int-int-int-) .简单值对象的构建器通常是过度工程的结果. (120认同)
  • 这是在2009年,`静态'在2015年几乎被认为是有害的.[类型安全流畅的`Builder`实例,执行完整和有效的建筑合同现在是一个更好的解决方案.](http://www.vertigrated.com /博客/ 2015/08 /流利的建设者图案再访/) (22认同)
  • @JarrodRoberson:很好,期待着它!我想要传达的内容:作为您博客的读者,一个50行示例,其中包含对正在发生的事情的简短文本描述,这将帮助我超过300行没有上下文. (17认同)
  • @JarrodRoberson:通过编译器强制正确使用的有趣方式,感谢分享!对未来帖子的友好建议:对于大多数人来说,300行未注释的源代码可能有点消化(毕竟代码比阅读更难阅读).再次感谢! (12认同)
  • @ user177800不同意-如果将静态方法编写为纯函数,则完全可以。当静态函数改变状态时,它们就会变成问题... (7认同)
  • @user177800 2020 年你的链接已经失效了。 (6认同)
  • 我想知道 (JarrodRoberson.equals(user177800)) 是否... (2认同)

Eli*_*ght 616

不,但您可以使用Builder Pattern,如此Stack Overflow答案中所述.

如链接答案中所述,Builder Pattern允许您编写类似的代码

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();
Run Code Online (Sandbox Code Playgroud)

其中某些字段可以具有默认值,或者是可选的.

  • 最后是一个不到2页的Builder模式示例. (129认同)
  • @ivanceras:另一个原因是你可能希望你的类在构造之后是不可变的,所以你不希望它中的方法改变它的值. (54认同)
  • @ivanceras:当类具有必需字段时,它是相关的,并且您不希望能够在无效状态下实例化这些类.所以,如果你只是说'学生s1 =新学生().年龄(16岁);`那么这会让你留下一个没有名字的学生,这可能是坏事.如果它还不错,那么你的解决方案很好. (51认同)
  • 我很好奇,为什么在使用构建器模式时我们需要构建器类.我在想学生s1 =新学生().名字("Spicolo").年龄(16).motto("Aloha,Mr Hand"); (14认同)
  • @ivanceras:我用建设者的3两件事 - 消灭多种说法,流畅的初始化,不变性,最重要的是我觉得,以验证在构建(域对象)method.Why创建一个对象实例,如果是invalid.You也可以重载静态工厂方法如上例中的buildFreshman(),buildSenior()等 (3认同)

Vit*_*nko 447

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

  1. 方法重载.

    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");
    
    Run Code Online (Sandbox Code Playgroud)

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

  2. 可变参数.

    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);
    
    Run Code Online (Sandbox Code Playgroud)

    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");
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    void foo(String a, Integer b, Integer c) {
        b = b != null ? b : 0;
        c = c != null ? c : 0;
        //...
    }
    
    foo("a", null, 2);
    
    Run Code Online (Sandbox Code Playgroud)

    现在必须提供所有参数值,但默认值可以为null.

  4. 可选课程.此方法类似于null,但对具有默认值的参数使用Java 8 Optional类:

    void foo(String a, Optional<Integer> bOpt) {
        Integer b = bOpt.isPresent() ? bOpt.get() : 0;
        //...
    }
    
    foo("a", Optional.of(2));
    foo("a", Optional.<Integer>absent());
    
    Run Code Online (Sandbox Code Playgroud)

    可选为调用者显示方法契约,但是,人们可能会发现这样的签名过于冗长.

  5. 生成器模式.构建器模式用于构造函数,并通过引入单独的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();
    
    Run Code Online (Sandbox Code Playgroud)
  6. 地图.当参数的数量太大时,对于大多数参数,通常使用默认值,您可以将方法参数作为其名称/值的映射传递:

    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")); 
    
    Run Code Online (Sandbox Code Playgroud)

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

  • @Celeritas`return this`返回调用该方法的同一对象(在例子中,`FooBuilder`).这允许在一个语句中对同一对象进行链接:`new FooBuilder().setA(..).setB(..).setC(..)`etc,而不是在单独的语句中调用每个方法. (3认同)
  • 很好的解释。我从未见过这样使用返回值。5)“return this”有什么作用?另外,`FooBuilder().setA("a").build();`不是因为(根据定义)首先调用构造函数并且`FooBuilder()`返回一个值,这是否意味着`.setA ("a"):` 没有机会被叫? (2认同)
  • @Celeritas`new FooBuilder()`返回一个`FooBuilder`对象,在该对象上调用`setA`方法.由于没有调用`setB`,`this.b`保留默认值.最后,在``FooBuilder`对象上调用`build`方法.`build`方法创建并返回一个`Foo`对象,该对象设置为变量`Foo foo`.请注意,`FooBuilder`对象不存储在任何变量中. (2认同)

Rob*_*b H 240

可悲的是没有.

  • @Trey:具有默认参数的语言通常会丢弃函数重载,因为它不那么引人注目.所以没有歧义.此外,Scala在2.8中添加了该功能,并以某种方式解决了模糊性问题(因为出于兼容性原因它们保持了重载). (68认同)
  • 是的,让我们进行权衡,让编译器做一些额外的工作,而是让我们都写出100000次重载,以便为我们的库用户提供便利.好主意. (49认同)
  • 难过吗?这样做会引入可能模糊的功能签名. (32认同)
  • 我没有看到参数默认值如何阻止函数重载.例如,C#允许覆盖,并允许默认初始化.似乎任意选择,不限制是原因. (32认同)
  • @ user562566:每当我处理一个Java项目时,我都会觉得Java开发人员是按照他们每天生成的代码行数来支付/衡量的 (22认同)
  • @Trey:了解Ada如何实现重载和默认参数以及Ada如何避免命名参数的模糊性.设计正确需要时间.Java团队没有的时间. - PhiLho:像Ada Scala一样命名参数.解决问题的简单方法.并且它们使代码更好地在顶部阅读. (11认同)
  • 多年来在C#中工作没有问题,甚至与方法重载相结合.编译器会显示有关冲突的警告,就是这样.做一些像writeToFile(String content,boolea append = false); (3认同)
  • 我认为参数默认是不好的做法,重载是一个更清洁的解决方案.您甚至可以隐藏默认构造函数并使用静态工厂方法返回初始化实例.这对IOC也很有用,您可以在其中确定要返回的接口的实现.如果要选择性地分配属性,创建公共属性或重载构造函数使用另一种方法来执行相同的功能并不会使其更好.它只是使语言更加神秘,而且只是懒惰的编程,这**是函数重载的原因之一. (2认同)
  • 我同意这很可悲。当我编写 Java API 供人们从 JRuby 调用时,我最终不得不让每个方法都采用 `Map&lt;?,?&gt;` 并将所有参数塞入映射中,以便可以编写惯用的 Ruby。我更喜欢参数名称和默认值,因为 Javadoc 会更好看。 (2认同)
  • 是的,确实很悲伤。几乎其他语言都没有问题(C#、Kotlin、Scala、Groovy、Typescript、Javascript...) (2认同)

ebe*_*sle 80

不幸的是,是的.

void MyParameterizedFunction(String param1, int param2, bool param3=false) {}
Run Code Online (Sandbox Code Playgroud)

可以用Java 1.5编写为:

void MyParameterizedFunction(String param1, int param2, Boolean... params) {
    assert params.length <= 1;
    bool param3 = params.length > 0 ? params[0].booleanValue() : false;
}
Run Code Online (Sandbox Code Playgroud)

但是,你是否应该依赖于你对编译器生成的感觉

new Boolean[]{}
Run Code Online (Sandbox Code Playgroud)

每次通话.

对于多个可默认参数:

void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}
Run Code Online (Sandbox Code Playgroud)

可以用Java 1.5编写为:

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;
}
Run Code Online (Sandbox Code Playgroud)

这符合C++语法,它只允许参数列表末尾的默认参数.

除了语法之外,还有一个不同之处在于它对传递的默认参数进行了运行时类型检查,而C++类型在编译期间检查它们.

  • 聪明,但是varargs(...)只能用于最终参数,这比支持默认参数的语言更有限. (14认同)
  • 绝不应该在生产代码中使用`assert`.抛出一个例外. (10认同)
  • 与C++版本相比,这很聪明但有点混乱 (6认同)
  • Java肯定需要可选的默认参数,因为C#和其他允许...语法很明显,我认为他们可以相当简单地实现这一点,即使只是编译所有可能的组合......我无法想象为什么他们没有将它添加到语言中然而! (5认同)
  • -1这真的不是varargs的用途.这是一个黑客. - 在这种情况下,使用重载将更具可读性(这是不幸的,因为三个额外的字符比5个额外的源代码行更具可读性......). - 但Java不支持默认参数. (5认同)
  • 我添加了一个多个默认参数的示例. (2认同)

pet*_*ica 37

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

public: void myFunction(int a, int b=5, string c="test") { ... }
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

之前提到过,默认参数导致函数重载的模糊情况.这根本不是真的,我们可以在C++的情况下看到:是的,也许它可以创建模糊的情况,但这些问题可以很容易地处理.它本身并不是用Java开发的,可能是因为创作者想要一种更简单的语言,因为C++是 - 如果他们有正确的话,则是另一个问题.但是我们大多数人并不认为他使用Java是因为它简单.

  • C#默认值表示法的主要思想正是为了避免这种样板编码而只有一个构造函数而不是很多. (6认同)
  • 每种编程语言(特别是)是避免汇编程序样板的手段,我错了吗?问题只在于它是否提供了便利的功能. (4认同)
  • 第一句话是一个修辞问题.第二句中的"问题"一词与第一句中的修辞问题无关. (2认同)
  • 更具体地说:语言是一种工具,允许我们以一种我们可以控制所写内容的方式编写程序,编译是一种告诉机器我们想要什么的方式。如果一个工具允许我们避免样板文件,它会更有用。实际上,gnavi 询问,他们是否可以准确地避免您建议作为答案的样板代码类型,因为 C# 允许这样做。 (2认同)

小智 22

您可以在Scala中执行此操作,Scala在JVM上运行并与Java程序兼容. http://www.scala-lang.org/

class Foo(var prime: Boolean = false, val rib: String)  {}
Run Code Online (Sandbox Code Playgroud)

  • 使用全新的语言来获得一个不那么常见的功能? (57认同)
  • @Val只是说这就像用大炮射击鸟 (28认同)
  • @ om-nom-nom:Java永远不会存在.说没有使用某个功能等同于没人需要它就是说Java在它发明之前并不流行意味着Gosling不应该开始设计它. (8认同)
  • 这与OP的问题无关 (8认同)
  • 也可以在 Kotlin 中工作。还有格罗维。还有 C#。还有 JavaScript。几乎所有其他语言都是为真实的人和问题而设计的。 (3认同)

小智 18

而不是使用:

void parameterizedMethod(String param1, int param2) {
    this(param1, param2, false);
}

void parameterizedMethod(String param1, int param2, boolean param3) {
    //use all three parameters here
}
Run Code Online (Sandbox Code Playgroud)

您可以通过一个方法来利用 java 的 Optional 功能:

void parameterizedMethod(String param1, int param2, @Nullable Boolean param3) {
    param3 = Optional.ofNullable(param3).orElse(false);
    //use all three parameters here
}
Run Code Online (Sandbox Code Playgroud)

主要区别在于您必须使用包装类而不是原始 Java 类型来允许null输入。Boolean而不是booleanInteger而不是int等等。


sim*_*eco 14

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

public myParameterizedFunction(String param1, int param2, Boolean param3) {

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}
Run Code Online (Sandbox Code Playgroud)

或者代替三元运算符,您可以使用:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,这种方法似乎是其他替代方法中最好的。对于 Java 来说,采用默认值仍然是件好事;Kotlin 表明这是可以做到的,所以我不确定为什么 Oracle 没有进入现代时代,而继续像 90 年代一样设计 java。:D (4认同)

小智 12

我可能会在这里说明显而易见但为什么不自己简单地实现"默认"参数?

public class Foo() {
        public void func(String s){
                func(s, true);
        }
        public void func(String s, boolean b){
                //your code here
        }
}
Run Code Online (Sandbox Code Playgroud)

默认情况下,您将使用以太网

func("我的字符串");

如果你不想使用默认值,你会使用

func("my string",false);

  • 海报问这个(相当丑陋的)模式是否可以避免... ;-)在更现代的语言(如c#,Scala)中,你不需要额外的重载,这只会产生更多的代码行.在某些时候你可以在此期间使用varargs(static int max(int ... array){}),但它们只是一个非常难看的解决方法. (10认同)
  • 重载并不丑陋并且具有许多好处,例如具有不同签名的不同方法调用可以执行不同的功能.`//这是更好的公共类Foo(){/*这样做*/public void func(String s){//做某事}/*这用b*/public void func做另外的事情(String s,boolean b){// b被传递}}``//比这个公共类Foo(){/*除非b = value,否则它会做一些事情然后它会做其他事情*/public void func(String s,boolean b = value ){If(b){// Do Something} else {// Do something others}}}` (2认同)

Vir*_*ngh 11

java 不支持它,而其他语言则不支持它。科特林。


mrt*_*rts 7

正如提到Scala一样,Kotlin也值得一提。在Kotlin函数中,参数也可以具有默认值,甚至可以引用其他参数:

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

与Scala一样,Kotlin可以在JVM上运行,并且可以轻松地集成到现有的Java项目中。


San*_*kak 6

没有.

您可以通过传递具有智能默认值的Object来实现相同的行为.但这又取决于你的情况.


tom*_*jen 6

不.通常,Java没有太多(任何)语法糖,因为他们试图制作一种简单的语言.

  • 不完全的.令人痛苦的事实是,球队的时间紧迫,没有时间用于语法糖.为什么还要`const`和`goto`是保留关键字哪个没有实现? - 尤其是`const`是我非常想念的东西 - "final"不是替代品,他们知道这一点. - 如果你做了*有意义的*决定永远不实现`goto`,你将不需要保留关键字. - 后来在Java团队作弊,使得基于标签的"break"和"continue"与Pascal`goto`一样强大. (26认同)