जावा में नामित पैरामीटर मुहावरे को कैसे लागू किया जाए? (विशेष रूप से निर्माणकर्ताओं के लिए)
मैं सिंटैक्स की तरह एक ऑब्जेक्टिव-सी की तलाश कर रहा हूं और JavaBeans में इस्तेमाल किए गए एक जैसे नहीं।
एक छोटा कोड उदाहरण ठीक होगा।
धन्यवाद।
जवाबों:
सबसे अच्छा जावा मुहावरा मैं निर्माण में खोजशब्द तर्कों का अनुकरण करने के लिए लगता है कि बिल्डर पैटर्न, प्रभावी जावा 2 संस्करण में वर्णित है ।
मूल विचार एक बिल्डर वर्ग के पास है जो विभिन्न निर्माण मापदंडों के लिए बसने वाले (लेकिन आमतौर पर गेटर्स नहीं है) है। इसका भी एक 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...
}
फू की एक मिसाल बनाने के लिए आप कुछ इस तरह लिखें:
Foo foo = Foo.builder()
.setColor(red)
.setName("Fred")
.setSize(42)
.build();
मुख्य चेतावनी हैं:
आप इस ब्लॉग पोस्ट को देखना चाहते हैं (मेरे द्वारा नहीं)।
.withFoo
, बजाय .setFoo
: newBuilder().withSize(1).withName(1).build()
के बजायnewBuilder().setSize(1).setName(1).build()
There's no compile-time checking that all of the parameters have been specified exactly once.
इस समस्या को वहाँ लौटने वाले इंटरफेस Builder1
से दूर किया जा सकता है, BuilderN
जहाँ प्रत्येक व्यक्ति बसने वालों में से एक को कवर करता है या build()
। यह कोड के लिए बहुत अधिक क्रिया है, लेकिन यह आपके डीएसएल के लिए संकलक समर्थन के साथ आता है और साथ काम करने के लिए ऑटो-पूर्ण बहुत अच्छा बनाता है।
यह उल्लेख के लायक है:
Foo foo = new Foo() {{
color = red;
name = "Fred";
size = 42;
}};
तथाकथित डबल-ब्रेस इनिशियलाइज़र । यह वास्तव में उदाहरण के साथ एक प्रारंभिक वर्ग है initializer।
आप यहां से सलाह का पालन करने की कोशिश कर सकते हैं: http://www.artima.com/weblogs/viewpost.jsp?thread/118828
int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);
यह कॉल साइट पर क्रिया है, लेकिन कुल मिलाकर सबसे कम ओवरहेड देता है।
doIt( /*value*/ 13, /*location*/ 47, /*overwrite*/ true )
जावा 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);
}
}
create()
मुझे मेरे पटरियों में बंद कर दिया। मैंने जावा में लैम्बडा की शैली को कभी नहीं देखा है। क्या आपने पहली बार लैम्बदास के साथ दूसरी भाषा में इस विचार की खोज की थी?
जावा कंस्ट्रक्टर या विधि तर्कों के लिए उद्देश्य-सी-जैसे नामित मापदंडों का समर्थन नहीं करता है। इसके अलावा, यह वास्तव में चीजों को करने का जावा तरीका नहीं है। जावा में, विशिष्ट पैटर्न को मौखिक रूप से कक्षाओं और सदस्यों का नाम दिया गया है। कक्षाएं और चर संज्ञाएं होनी चाहिए और नाम वाली विधि क्रियाएं होनी चाहिए। मुझे लगता है कि आप रचनात्मक हो सकते हैं और जावा नामकरण सम्मेलनों से विचलित हो सकते हैं और एक हैक तरीके से ऑब्जेक्टिव-सी प्रतिमान का अनुकरण कर सकते हैं, लेकिन यह आपके कोड को बनाए रखने के साथ चार्ज किए गए औसत जावा डेवलपर द्वारा विशेष रूप से सराहना नहीं की जाएगी। किसी भी भाषा में काम करते समय, यह आपको भाषा और समुदाय के सम्मेलनों से चिपके रहने के लिए प्रेरित करता है, खासकर जब एक टीम पर काम कर रहा हो।
यदि आप जावा 6 का उपयोग कर रहे हैं, तो आप चर मापदंडों का उपयोग कर सकते हैं और स्थैतिक को बेहतर परिणाम देने के लिए आयात कर सकते हैं। इसका विवरण इसमें पाया गया है:
http://zinzel.blogspot.com/2010/07/creating-methods-with-onym-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));
मैं कहना है कि यह शैली पतों दोनों चाहते हैं नामित पैरामीटर और गुण के बिना सुविधाओं प्राप्त और सेट उपसर्ग जो अन्य भाषा है। जावा दायरे में इसकी पारंपरिक नहीं, बल्कि इसकी सरलता, समझने में मुश्किल नहीं है, खासकर यदि आपने अन्य भाषाओं को संभाला है।
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
println(jacobi.name());
println(jacobi.age());
//...
jacobi.name("Lemuel Jacobi");
jacobi.age(4);
println(jacobi.name());
println(jacobi.age());
}
}
व्हाट अबाउट
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));
वास्तविक नामित मापदंडों की तुलना में सीमाएं:
/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2)
)यदि आपके पास स्काला 2.8 का विकल्प है। http://www.scala-lang.org/node/2075
not really better than a comment
आगे
जावा 8 के लैम्ब्डा का उपयोग करके आप वास्तविक नामित मापदंडों के करीब भी पहुंच सकते हैं ।
foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
ध्यान दें कि यह संभवतः एक युगल दर्जन "जावा सर्वोत्तम प्रथाओं" का उल्लंघन करता है (जैसे कि कुछ भी जो $
प्रतीक का उपयोग करता है )।
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));
}
}
पेशेवरों:
विपक्ष:
$foo
कभी भी कॉल करने वाले के पास नहीं भागता (जब तक कि कोई उसे कॉलबैक के अंदर वैरिएबल को असाइन नहीं करता) तब तक वे सार्वजनिक क्यों नहीं हो सकते ?
जावा में नामित मापदंडों का अनुकरण करने के लिए आप प्रोजेक्ट लोनोक के @Builder एनोटेशन का उपयोग कर सकते हैं । यह आपके लिए एक बिल्डर उत्पन्न करेगा जिसका उपयोग आप किसी भी वर्ग (आपके द्वारा लिखी गई कक्षाओं और बाहरी पुस्तकालयों से आने वाले दोनों वर्गों) के नए उदाहरण बनाने के लिए कर सकते हैं।
यह एक वर्ग पर इसे सक्षम करने का तरीका है:
@Getter
@Builder
public class User {
private final Long id;
private final String name;
}
बाद में आप इसका उपयोग कर सकते हैं:
User userInstance = User.builder()
.id(1L)
.name("joe")
.build();
यदि आप लाइब्रेरी से आने वाले वर्ग के लिए इस तरह का एक बिल्डर बनाना चाहते हैं, तो इस तरह एक एनोटेट स्टेटिक विधि बनाएं:
class UserBuilder {
@Builder(builderMethodName = "builder")
public static LibraryUser newLibraryUser(Long id, String name) {
return new LibraryUser(id, name);
}
}
यह "बिल्डर" नामक एक विधि उत्पन्न करेगा जिसे इसके द्वारा बुलाया जा सकता है:
LibraryUser user = UserBuilder.builder()
.id(1L)
.name("joe")
.build();
मुझे लगता है कि "टिप्पणी-वर्कअराउंड" का यह हकदार है कि यह खुद का जवाब है (मौजूदा उत्तरों में छिपा हुआ है और यहां टिप्पणियों में उल्लेख किया गया है)।
someMethod(/* width */ 1024, /* height */ 768);
यह एक प्रकार है Builder
लॉरेंस द्वारा ऊपर वर्णित पैटर्न ।
मैं खुद को इसका इस्तेमाल करते हुए बहुत (उपयुक्त स्थानों पर) पाता हूं।
मुख्य अंतर यह है, कि इस मामले में बिल्डर अपरिवर्तनीय है । इसका फायदा यह है कि इसका पुन: उपयोग किया जा सकता है और यह थ्रेड-सुरक्षित है।
तो आप इसका उपयोग एक डिफ़ॉल्ट बिल्डर बनाने के लिए कर सकते हैं और फिर विभिन्न स्थानों पर करने के लिए कर सकते हैं जहां आपको इसकी आवश्यकता है आप इसे कॉन्फ़िगर कर सकते हैं और अपनी वस्तु का निर्माण कर सकते हैं।
यह सबसे अधिक समझ में आता है, यदि आप एक ही वस्तु को बार-बार बना रहे हैं, क्योंकि तब आप बिल्डर को स्थिर बना सकते हैं और इसे सेटिंग्स बदलने के बारे में चिंता करने की ज़रूरत नहीं है।
दूसरी ओर अगर आपको बदलते पैरामाटर्स के साथ वस्तुओं का निर्माण करना है तो इससे कुछ ओवरहेड शांत हो जाता है। (लेकिन हे, आप कस्टम 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();
}
}
जावा में कोई समाधान होने की संभावना बहुत वर्बोज़ होने जा रहा है, लेकिन यह की कीमत की तरह है कि उपकरणों का उल्लेख गूगल AutoValues और Immutables बिल्डर वर्गों आप स्वचालित रूप से JDK संकलन समय एनोटेशन प्रसंस्करण प्रयोग करने के लिए उत्पन्न होगा।
मेरे मामले के लिए, मैं एक जावा एनम में उपयोग करने के लिए पैरामीटर नामित करना चाहता था, इसलिए एक बिल्डर पैटर्न काम नहीं करेगा क्योंकि एनम इंस्टेंसेस को अन्य वर्गों द्वारा त्वरित नहीं किया जा सकता है। मैं इसी तरह के बहरे के जवाब के साथ एक दृष्टिकोण के साथ आया था, लेकिन पैरामीटर ऑर्डरिंग के संकलन-समय की जाँच को जोड़ता है (अधिक कोड की कीमत पर)
यहाँ ग्राहक कोड है:
Person p = new Person( age(16), weight(100), heightInches(65) );
और कार्यान्वयन:
class Person {
static class TypedContainer<T> {
T val;
TypedContainer(T val) { this.val = val; }
}
static Age age(int age) { return new Age(age); }
static class Age extends TypedContainer<Integer> {
Age(Integer age) { super(age); }
}
static Weight weight(int weight) { return new Weight(weight); }
static class Weight extends TypedContainer<Integer> {
Weight(Integer weight) { super(weight); }
}
static Height heightInches(int height) { return new Height(height); }
static class Height extends TypedContainer<Integer> {
Height(Integer height) { super(height); }
}
private final int age;
private final int weight;
private final int height;
Person(Age age, Weight weight, Height height) {
this.age = age.val;
this.weight = weight.val;
this.height = height.val;
}
public int getAge() { return age; }
public int getWeight() { return weight; }
public int getHeight() { return height; }
}
करग पुस्तकालय द्वारा समर्थित मुहावरे पर विचार करने योग्य हो सकता है:
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");
}
}
R Casha
उत्तर के समान है लेकिन इसे समझाने के लिए कोड के बिना है।
यहां एक कंपाइलर-चेक बिल्डर पैटर्न है। कैविट्स:
.build()
विधि नहीं हो सकतीतो आपको कक्षा के बाहर कुछ ऐसा चाहिए जो पास न होने पर असफल हो जाए Builder<Yes, Yes, Yes>
। getSum
उदाहरण के रूप में स्थैतिक विधि देखें ।
class No {}
class Yes {}
class Builder<K1, K2, K3> {
int arg1, arg2, arg3;
Builder() {}
static Builder<No, No, No> make() {
return new Builder<No, No, No>();
}
@SuppressWarnings("unchecked")
Builder<Yes, K2, K3> arg1(int val) {
arg1 = val;
return (Builder<Yes, K2, K3>) this;
}
@SuppressWarnings("unchecked")
Builder<K1, Yes, K3> arg2(int val) {
arg2 = val;
return (Builder<K1, Yes, K3>) this;
}
@SuppressWarnings("unchecked")
Builder<K1, K2, Yes> arg3(int val) {
this.arg3 = val;
return (Builder<K1, K2, Yes>) this;
}
static int getSum(Builder<Yes, Yes, Yes> build) {
return build.arg1 + build.arg2 + build.arg3;
}
public static void main(String[] args) {
// Compiles!
int v1 = getSum(make().arg1(44).arg3(22).arg2(11));
// Builder.java:40: error: incompatible types:
// Builder<Yes,No,Yes> cannot be converted to Builder<Yes,Yes,Yes>
int v2 = getSum(make().arg1(44).arg3(22));
System.out.println("Got: " + v1 + " and " + v2);
}
}
कैविट्स ने समझाया । कोई निर्माण विधि क्यों? मुसीबत यह है कि यह Builder
कक्षा में होने जा रहा है , और इसे इसके साथ मानकीकृत किया जाएगाK1, K2, K3
, आदि। जैसा कि विधि को खुद को संकलित करना है, सब कुछ इसे कॉल करना होगा। इसलिए, आम तौर पर, हम कक्षा की एक विधि में एक संकलन परीक्षा नहीं दे सकते हैं।
इसी तरह के कारण से, हम बिल्डर मॉडल का उपयोग करके दोहरे असाइनमेंट को रोक नहीं सकते हैं।
@irreputable एक अच्छा समाधान लेकर आया। हालांकि - यह आपकी कक्षा को अमान्य स्थिति में छोड़ सकता है, क्योंकि कोई सत्यापन और संगतता जाँच नहीं होगी। इसलिए मैं इसे बिल्डर समाधान के साथ जोड़ना पसंद करता हूं, अतिरिक्त उपवर्ग को बनाए जाने से बचता हूं, हालांकि यह अभी भी बिल्डर वर्ग को उपवर्ग में रखेगा। इसके अतिरिक्त, क्योंकि अतिरिक्त बिल्डर वर्ग इसे और अधिक क्रियाशील बनाता है, इसलिए मैंने लैम्बडा का उपयोग करके एक और विधि जोड़ी। मैंने पूर्णता के लिए कुछ अन्य बिल्डर दृष्टिकोण जोड़े।
निम्नानुसार एक वर्ग से शुरू करना:
public class Foo {
static public class Builder {
public int size;
public Color color;
public String name;
public Builder() { size = 0; color = Color.RED; name = null; }
private Builder self() { return this; }
public Builder size(int size) {this.size = size; return self();}
public Builder color(Color color) {this.color = color; return self();}
public Builder name(String name) {this.name = name; return self();}
public Foo build() {return new Foo(this);}
}
private final int size;
private final Color color;
private final String name;
public Foo(Builder b) {
this.size = b.size;
this.color = b.color;
this.name = b.name;
}
public Foo(java.util.function.Consumer<Builder> bc) {
Builder b = new Builder();
bc.accept(b);
this.size = b.size;
this.color = b.color;
this.name = b.name;
}
static public Builder with() {
return new Builder();
}
public int getSize() { return this.size; }
public Color getColor() { return this.color; }
public String getName() { return this.name; }
}
फिर इसका उपयोग विभिन्न तरीकों को लागू करने में:
Foo m1 = new Foo(
new Foo.Builder ()
.size(1)
.color(BLUE)
.name("Fred")
);
Foo m2 = new Foo.Builder()
.size(1)
.color(BLUE)
.name("Fred")
.build();
Foo m3 = Foo.with()
.size(1)
.color(BLUE)
.name("Fred")
.build();
Foo m4 = new Foo(
new Foo.Builder() {{
size = 1;
color = BLUE;
name = "Fred";
}}
);
Foo m5 = new Foo(
(b)->{
b.size = 1;
b.color = BLUE;
b.name = "Fred";
}
);
यह @LaurenceGonsalves पहले से ही पोस्ट किए गए भाग से कुल चीर-फाड़ की तरह दिखता है, लेकिन आपको चुने गए सम्मेलन में छोटे अंतर दिखाई देंगे।
मुझे आश्चर्य है, अगर जेएलएस कभी नामित मापदंडों को लागू करेगा, तो वे इसे कैसे करेंगे? क्या वे मौजूदा मुहावरों में से एक पर इसके लिए एक संक्षिप्त रूप प्रदान करके अपना विस्तार करेंगे? इसके अलावा स्काला कैसे नामित मापदंडों का समर्थन करता है?
हम्म - अनुसंधान के लिए पर्याप्त है, और शायद एक नया सवाल।