Java 中的命名参数习语

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1988016/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:08:29  来源:igfitidea点击:

Named Parameter idiom in Java

javaidioms

提问by Red Hyena

How to implement Named Parameter idiom in Java? (especially for constructors)

如何在 Java 中实现命名参数习语?(特别是对于构造函数)

I am looking for an Objective-C like syntax and not like the one used in JavaBeans.

我正在寻找一种类似于 Objective-C 的语法,而不是像 JavaBeans 中使用的那种。

A small code example would be fine.

一个小的代码示例就可以了。

Thanks.

谢谢。

采纳答案by Laurence Gonsalves

The best Java idiom I've seem for simulating keyword arguments in constructors is the Builder pattern, described in Effective Java 2nd Edition.

我认为在构造函数中模拟关键字参数的最佳 Java 习惯用法是 Builder 模式,在Effective Java 2nd Edition 中进行了描述。

The basic idea is to have a Builder class that has setters (but usually not getters) for the different constructor parameters. There's also a build()method. The Builder class is often a (static) nested class of the class that it's used to build. The outer class's constructor is often private.

基本思想是拥有一个 Builder 类,该类具有用于不同构造函数参数的 setter(但通常没有 getter)。还有一个build()方法。Builder 类通常是它用于构建的类的(静态)嵌套类。外部类的构造函数通常是私有的。

The end result looks something like:

最终结果类似于:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setSize(int size) {
      this.size = size;
      return this;
    }

    public Builder setColor(Color color) {
      this.color = color;
      return this;
    }

    public Builder setName(String name) {
      this.name = name;
      return this;
    }

    // you can set defaults for these here
    private int size;
    private Color color;
    private String name;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    size = builder.size;
    color = builder.color;
    name = builder.name;
  }

  private final int size;
  private final Color color;
  private final String name;

  // The rest of Foo goes here...
}

To create an instance of Foo you then write something like:

要创建 Foo 的实例,您可以编写如下内容:

Foo foo = Foo.builder()
    .setColor(red)
    .setName("Fred")
    .setSize(42)
    .build();

The main caveats are:

主要的注意事项是:

  1. Setting up the pattern is pretty verbose (as you can see). Probably not worth it except for classes you plan on instantiating in many places.
  2. There's no compile-time checking that all of the parameters have been specified exactly once. You can add runtime checks, or you can use this only for optional parameters and make required parameters normal parameters to either Foo or the Builder's constructor. (People generally don't worry about the case where the same parameter is being set multiple times.)
  1. 设置模式非常冗长(如您所见)。除了您计划在许多地方实例化的类之外,可能不值得。
  2. 没有编译时检查所有参数是否都被指定了一次。您可以添加运行时检查,或者您可以仅将其用于可选参数,并使必需参数成为 Foo 或 Builder 构造函数的普通参数。(人们一般不会担心多次设置相同参数的情况。)

You may also want to check out this blog post(not by me).

您可能还想查看这篇博文(不是我写的)。

回答by Asaph

Java does not support Objective-C-like named parameters for constructors or method arguments. Furthermore, this is really not the Java way of doing things. In java, the typical pattern is verbosely named classes and members. Classes and variables should be nouns and method named should be verbs. I suppose you could get creative and deviate from the Java naming conventions and emulate the Objective-C paradigm in a hacky way but this wouldn't be particularly appreciated by the average Java developer charged with maintaining your code. When working in any language, it behooves you to stick to the conventions of the language and community, especially when working on a team.

Java 不支持构造函数或方法参数的类似 Objective-C 的命名参数。此外,这真的不是 Java 的做事方式。在 Java 中,典型的模式是详细命名类和成员。类和变量应该是名词,命名的方法应该是动词。我想您可以发挥创意并偏离 Java 命名约定,并以一种骇人听闻的方式模拟 Objective-C 范式,但是负责维护您的代码的普通 Java 开发人员不会特别欣赏这一点。在使用任何语言工作时,您都应该遵守语言和社区的约定,尤其是在团队中工作时。

回答by irreputable

This is worth of mentioning:

这是值得一提的:

Foo foo = new Foo() {{
    color = red;
    name = "Fred";
    size = 42;
}};

the so called double-brace initializer. It is actually an anonymous class with instance initializer.

所谓的双括号初始化器。它实际上是一个带有实例初始值设定项的匿名类。

回答by deamon

You could use a usual constructor and static methods that give the arguments a name:

您可以使用通常的构造函数和静态方法来为参数命名:

public class Something {

    String name;
    int size; 
    float weight;

    public Something(String name, int size, float weight) {
        this.name = name;
        this.size = size;
        this.weight = weight;
    }

    public static String name(String name) { 
        return name; 
    }

    public static int size(int size) {
        return size;
    }

    public float weight(float weight) {
        return weight;
    }

}

Usage:

用法:

import static Something.*;

Something s = new Something(name("pen"), size(20), weight(8.2));

Limitations compared to real named parameters:

与真实命名参数相比的局限性:

  • argument order is relevant
  • variable argument lists are not possible with a single constructor
  • you need a method for every argument
  • not really better than a comment (new Something(/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2))
  • 参数顺序是相关的
  • 单个构造函数无法实现可变参数列表
  • 每个参数都需要一个方法
  • 并不比评论更好(新的东西(/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2)

If you have the choice look at Scala 2.8. http://www.scala-lang.org/node/2075

如果您有选择,请查看 Scala 2.8。http://www.scala-lang.org/node/2075

回答by R Casha

If you are using Java 6, you can use the variable parameters and import static to produce a much better result. Details of this are found in:

如果您使用的是 Java 6,则可以使用可变参数并导入静态来产生更好的结果。详情见:

http://zinzel.blogspot.com/2010/07/creating-methods-with-named-parameters.html

http://zinzel.blogspot.com/2010/07/creating-methods-with-named-parameters.html

In short, you could have something like:

简而言之,你可以有类似的东西:

go();
go(min(0));
go(min(0), max(100));
go(max(100), min(0));
go(prompt("Enter a value"), min(0), max(100));

回答by user564819

What about

关于什么

public class Tiger {
String myColor;
int    myLegs;

public Tiger color(String s)
{
    myColor = s;
    return this;
}

public Tiger legs(int i)
{
    myLegs = i;
    return this;
}
}

Tiger t = new Tiger().legs(4).color("striped");

回答by Dominic Fox

The idiom supported by the karg librarymay be worth considering:

karg 库支持的成语可能值得考虑:

class Example {

    private static final Keyword<String> GREETING = Keyword.newKeyword();
    private static final Keyword<String> NAME = Keyword.newKeyword();

    public void greet(KeywordArgument...argArray) {
        KeywordArguments args = KeywordArguments.of(argArray);
        String greeting = GREETING.from(args, "Hello");
        String name = NAME.from(args, "World");
        System.out.println(String.format("%s, %s!", greeting, name));
    }

    public void sayHello() {
        greet();
    }

    public void sayGoodbye() {
        greet(GREETING.of("Goodbye");
    }

    public void campItUp() {
        greet(NAME.of("Sailor");
    }
}

回答by rkj

You could also try to follow advise from here: http://www.artima.com/weblogs/viewpost.jsp?thread=118828

您也可以尝试从这里遵循建议:http: //www.artima.com/weblogs/viewpost.jsp?thread=118828

int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);

It's verbose on the call site, but overall gives the lowest overhead.

它在呼叫站点上很冗长,但总体上提供了最低的开销。

回答by Alex

Java 8 style:

Java 8 风格:

public class Person {
    String name;
    int age;

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    static PersonWaitingForName create() {
        return name -> age -> new Person(name, age);
    }

    static interface PersonWaitingForName {
        PersonWaitingForAge name(String name);
    }

    static interface PersonWaitingForAge {
        Person age(int age);
    }

    public static void main(String[] args) {

        Person charlotte = Person.create()
            .name("Charlotte")
            .age(25);

    }
}
  • named parameters
  • fix order of arguments
  • static check -> no nameless Person possible
  • hard to switch arguments of same type by accident (like it is possible in telescop constructors)
  • 命名参数
  • 固定参数顺序
  • 静态检查 -> 没有无名的人可能
  • 很难意外地切换相同类型的参数(就像在伸缩构造函数中是可能的)

回答by Scheintod

This is a variant of the BuilderPattern as described by Lawrence above.

这是Builder上述劳伦斯所描述的模式的变体。

I find myself using this a lot (at the apropriate places).

我发现自己经常使用它(在适当的地方)。

The main difference is, that in this case the Builder is immuatable. This has the advantage that it can be reusedand is thread-safe.

主要区别在于,在这种情况下 Builder 是immutable。这样做的好处是可以重用并且是线程安全的。

So you can use this to make one default Builderand then in the various places where you need it you can configure it and build your object.

所以你可以使用它来制作一个默认的 Builder,然后在你需要它的各个地方你可以配置它并构建你的对象。

This makes most sense, if you are building the same object over and over again, because then you can make the builder static and don't have to worry about changing it's settings.

如果您一遍又一遍地构建同一个对象,这是最有意义的,因为这样您就可以使构建器保持静态,而不必担心更改它的设置。

On the other hand if you have to build objects with changing paramaters this has quiet some overhead. (but hey, you can combine static / dynamic generation with custom buildmethods)

另一方面,如果您必须使用不断变化的参数来构建对象,这会带来一些开销。(但是,嘿,您可以将静态/动态生成与自定义build方法结合起来)

Here is the example code:

这是示例代码:

public class Car {

    public enum Color { white, red, green, blue, black };

    private final String brand;
    private final String name;
    private final Color color;
    private final int speed;

    private Car( CarBuilder builder ){
        this.brand = builder.brand;
        this.color = builder.color;
        this.speed = builder.speed;
        this.name = builder.name;
    }

    public static CarBuilder with() {
        return DEFAULT;
    }

    private static final CarBuilder DEFAULT = new CarBuilder(
            null, null, Color.white, 130
    );

    public static class CarBuilder {

        final String brand;
        final String name;
        final Color color;
        final int speed;

        private CarBuilder( String brand, String name, Color color, int speed ) {
            this.brand = brand;
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        public CarBuilder brand( String newBrand ) {
            return new CarBuilder( newBrand, name, color, speed );
        }
        public CarBuilder name( String newName ) {
            return new CarBuilder( brand, newName, color, speed );
        }
        public CarBuilder color( Color newColor ) {
            return new CarBuilder( brand, name, newColor, speed );
        }
        public CarBuilder speed( int newSpeed ) {
            return new CarBuilder( brand, name, color, newSpeed );
        }
        public Car build() {
            return new Car( this );
        }
    }

    public static void main( String [] args ) {

        Car porsche = Car.with()
                .brand( "Porsche" )
                .name( "Carrera" )
                .color( Color.red )
                .speed( 270 )
                .build()
                ;

        // -- or with one default builder

        CarBuilder ASSEMBLY_LINE = Car.with()
                .brand( "Jeep" )
                .name( "Cherokee" )
                .color( Color.green )
                .speed( 180 )
                ;

        for( ;; ) ASSEMBLY_LINE.build();

        // -- or with custom default builder:

        CarBuilder MERCEDES = Car.with()
                .brand( "Mercedes" )
                .color( Color.black )
                ;

        Car c230 = MERCEDES.name( "C230" ).speed( 180 ).build(),
            clk = MERCEDES.name( "CLK" ).speed( 240 ).build();

    }
}