在Java中命名参数成语

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

我正在寻找Objective-C的语法,而不是JavaBeans中使用的语法。

一个小代码的例子会很好。

谢谢。

我看起来用来模拟构造函数中的关键字参数的最好的Java成语是Builder Java模式,在Effective Java 2nd Edition中有描述。

基本的想法是有一个Builder类,它为不同的构造参数提供了setter(但通常不是getter)。 还有一个build()方法。 Builder类通常是它用来构build的类的(静态)嵌套类。 外部类的构造函数通常是私有的。

最终结果如下所示:

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

为了创build一个Foo的实例,你可以这样写:

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

主要的注意事项是:

  1. 设置模式相当冗长(如你所见)。 除了你计划在许多地方实例化的课程,可能不值得。
  2. 没有编译时检查所有的参数都被指定了一次。 您可以添加运行时检查,也可以将其仅用于可选参数,并将所需参数的正常参数设置为Foo或Builder的构造函数。 (人们通常不用担心多次设置相同参数的情况。)

你也可能想看看这个博客文章 (不是由我)。

这值得一提:

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

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

你也可以尝试按照这里的build议: http : //www.artima.com/weblogs/viewpost.jsp?thread=118828

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

它在呼叫站点是冗长的,但总体上给出最低的开销。

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); } } 
  • 命名参数
  • 修正参数的顺序
  • 静态检查 – >没有名字的人可能的
  • 很难切换相同types的参数(例如在可伸缩的构造函数中是可能的)

如果您使用的是Java 6,则可以使用variables参数并导入静态以产生更好的结果。 这个的详细资料可以在

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

总之,你可以有这样的事情:

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

下面是Joshua Bloch的Effective Java中给出的技术的一些变化。 在这里,我试图使客户端代码更具可读性(或者更多的DSL)。

 /** * Actual class for which we want to implement a * named-parameter pseudo-constructor */ class Window{ protected int x, y, width, height; protected boolean isResizable; protected String title; public void show(){ // Show the window System.out.printf("Window \"%s\" set visible.%n",title); } /** * This class is only used to set the parameter values */ static class HavingProperties extends Window{ public HavingProperties x(int value){ this.x=value; return this; } public HavingProperties y(int value){ this.y=value; return this; } public HavingProperties width(int value){ this.width=value; return this; } public HavingProperties height(int value){ this.height=value; return this; } public HavingProperties resizable(boolean value){ this.isResizable=value; return this; } public HavingProperties title(String value){ this.title=value; return this; } } } public class NamedParameterIdiomInAction { public static void main(String... args){ Window window=new Window.HavingProperties().x(10).y(10).width(100). height(100).resizable(true).title("My App"); window.show(); } } 

请注意,通过这个变体,你也可以为伪构造函数赋予有意义的名字。

Java不支持构造函数或方法参数的Objective-C类命名参数。 而且,这实际上不是Java做事的方式。 在java中,典型的模式是名称类和成员。 类和variables应该是名词,命名的方法应该是动词。 我想你可以创造性地偏离Java的命名约定,并以一种拙劣的方式模拟Objective-C范例,但是这对于负责维护代码的普通Java开发人员来说并不是特别的赞赏。 用任何语言工作时,都应该坚持语言和社区的习惯,特别是在团队工作的时候。

关于什么

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

你可以使用通常的构造函数和静态方法给参数一个名字:

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

用法:

 import static Something.*; Something s = new Something(name("pen"), size(20), weight(8.2)); 

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

  • 参数顺序是相关的
  • variables参数列表不可能用一个构造函数
  • 你需要每个参数的方法
  • (新的东西( /*name*/ "pen", /*size*/ 20, /*weight*/ 8.2)

如果你有select看Scala 2.8。 http://www.scala-lang.org/node/2075

我想指出的是,这种风格同时解决了命名参数属性特性,没有其他语言所具有的getset前缀。 它在Java领域不是常规的,但它更简单,不难理解,特别是如果你已经处理了其他语言。

 public class Person { String name; int age; // name property // getter public String name() { return name; } // setter public Person name(String val) { name = val; return this; } // age property // getter public int age() { return age; } // setter public Person age(int val) { age = val; return this; } public static void main(String[] args) { // Addresses named parameter Person jacobi = new Person().name("Jacobi").age(3); // Addresses property style System.out.println(jacobi.name()); System.out.println(jacobi.age()); //... jacobi.name("Lemuel Jacobi"); jacobi.age(4); System.out.println(jacobi.name()); System.out.println(jacobi.age()); } } 

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

这是上面Lawrence所描述的Builder模式的一个变种。

我发现自己使用了很多(在适当的地方)。

主要的区别是,在这种情况下,Builder是免费的 。 这具有可重用并且是线程安全的优点。

所以你可以使用这个来创build一个默认的Builder ,然后在你需要的地方你可以configuration它并build立你的对象。

这是最有意义的,如果你是一遍又一遍地构build相同的对象,因为那样你就可以使构build器变成静态的,不必担心改变它的设置。

另一方面,如果您必须通过更改参数来构build对象,则会消除一些开销。 (但是,嘿,你可以结合静态/dynamic生成与自定义build方法)

以下是示例代码:

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

使用Java 8的lambdas你可以更接近真正的命名参数。

 foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};}); 

请注意,这可能违反了几十个“Java最佳实践”(就像任何使用$符号的东西)。

 public class Main { public static void main(String[] args) { // Usage foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};}); // Compare to roughly "equivalent" python call // foo(foo = -10, bar = "hello", array = [1, 2, 3, 4]) } // Your parameter holder public static class $foo { private $foo() {} public int foo = 2; public String bar = "test"; public int[] array = new int[]{}; } // Some boilerplate logic public static void foo(Consumer<$foo> c) { $foo foo = new $foo(); c.accept(foo); foo_impl(foo); } // Method with named parameters private static void foo_impl($foo par) { // Do something with your parameters System.out.println("foo: " + par.foo + ", bar: " + par.bar + ", array: " + Arrays.toString(par.array)); } } 

优点:

  • 比迄今为止所见的任何build筑模式都要短得多
  • 适用于方法和构造函数
  • 完全types安全
  • 它看起来非常接近其他编程语言中的实际命名参数
  • 它和典型的build造者模式一样安全(可以多次设置参数)

缺点:

  • 你的老板可能会为此私下化你的
  • 很难说出发生了什么事情