在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();
主要的注意事项是:
- 设置模式相当冗长(如你所见)。 除了你计划在许多地方实例化的课程,可能不值得。
- 没有编译时检查所有的参数都被指定了一次。 您可以添加运行时检查,也可以将其仅用于可选参数,并将所需参数的正常参数设置为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
我想指出的是,这种风格同时解决了命名参数和属性特性,没有其他语言所具有的get和set前缀。 它在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造者模式一样安全(可以多次设置参数)
缺点:
- 你的老板可能会为此私下化你的
- 很难说出发生了什么事情