Java에서 명명된 매개 변수 관용어
Java에서 명명된 매개 변수 관용구를 구현하는 방법은 무엇입니까?(특히 건설업자의 경우)
JavaBeans에서 사용되는 구문과 다른 Objective-C와 같은 구문을 찾고 있습니다.
작은 코드 예시로 충분합니다.
컨스트럭터에서 키워드 인수를 시뮬레이션하기 위해 내가 본 최고의 자바 관용어는 효과적인 Java 2nd Edition에서 설명된 Builder 패턴이다.
기본 개념은 다양한 생성자 매개 변수에 대한 설정자(일반적으로 getters는 포함 안 함)를 가진 Builder 클래스를 갖는 것입니다.'아주머니'라는 것도 있어요.build() 클래스는 위해 되는 클래스의(가 많습니다Builder 클래스는 빌드하는 데 사용되는 클래스의 (정적) 중첩된 클래스인 경우가 많습니다.외부 클래스의 생성자는 종종 비공개입니다.
최종 결과는 다음과 같습니다.
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 = Foo.builder()
.setColor(red)
.setName("Fred")
.setSize(42)
.build();
주요 경고는 다음과 같습니다.
- 패턴을 설정하는 것은 매우 장황합니다(보다시피).아마 많은 장소에서 인스턴스화할 계획인 수업을 제외하고는 그럴 가치가 없을 것이다.
- 모든 파라미터가 정확히1회 지정되었는지 컴파일 타임 체크는 없습니다.런타임 체크를 추가할 수도 있고, 옵션 파라미터에 대해서만 사용할 수도 있고, 필요한 파라미터를 Foo 또는 Builder의 컨스트럭터에 대해 일반 파라미터로 설정할 수도 있습니다(일반적으로 동일한 파라미터가 여러 번 설정될 경우 걱정하지 않습니다).
(제가 아닌) 이 블로그 투고도 확인해 보시기 바랍니다.
이것은 언급할 가치가 있습니다.
Foo foo = new Foo() {{
color = red;
name = "Fred";
size = 42;
}};
이른바 이중 결합 이니셜라이저입니다.실제로는 인스턴스 이니셜라이저를 사용하는 익명 클래스입니다.
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);
}
}
- 명명된 파라미터
- 의론의 순서를 정하다
- static check -> 이름 없는 사람은 불가능합니다.
- 우연히 같은 타입의 주장을 바꾸기가 어렵다(텔레스코프 컨스트럭터에서 가능한 것처럼).
또한 http://www.artima.com/weblogs/viewpost.jsp?thread=118828에서 조언을 받아보실 수도 있습니다.
int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);
콜 사이트에서는 상세하지만 전체적으로 가장 낮은 오버헤드를 제공합니다.
이 스타일은 다른 언어의 get 및 set prefix 없이 명명된 파라미터와 속성 기능을 모두 다루고 있음을 지적하고 싶습니다.Java 영역에서는 일반적이지 않지만 특히 다른 언어를 취급한 적이 있는 경우 더 단순하고 짧습니다.
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 Adane").age(3);
// addresses property style
System.out.println(jacobi.name());
System.out.println(jacobi.age());
// updates property values
jacobi.name("Lemuel Jacobi Adane");
jacobi.age(4);
System.out.println(jacobi.name());
System.out.println(jacobi.age());
}
}
Java 6을 사용하는 경우 변수 파라미터를 사용하여 스태틱을 Import하여 보다 나은 결과를 얻을 수 있습니다.상세한 것에 대하여는, 다음을 참조처를 참조해 주세요.
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));
어때
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");
Java는 생성자 또는 메서드 인수에 대해 Objective-C와 같은 명명된 매개 변수를 지원하지 않습니다.게다가 이것은 Java의 작업 방식과는 다릅니다.Java에서 일반적인 패턴은 상세하게 명명된 클래스와 멤버입니다.클래스 및 변수는 명사여야 하고 명명된 방법은 동사여야 합니다.창의력을 발휘하여 Java 명명규칙에서 벗어나 Objective-C 패러다임을 해킹 방식으로 에뮬레이트할 수 있다고 생각합니다만, 코드 유지보수를 담당하는 일반적인 Java 개발자에게는 그다지 감사하지 않을 것입니다.어떤 언어로든 일할 때는 특히 팀에서 일할 때 언어와 커뮤니티의 관습을 고수해야 합니다.
「코멘트 회피책」은, 독자적인 회답(기존의 회답에 숨겨져, 여기에 코멘트에 기재되어 있는 것)을 받을 자격이 있다고 생각합니다.
someMethod(/* width */ 1024, /* height */ 768);
인수에 이름을 부여하는 일반적인 생성자 및 정적 메서드를 사용할 수 있습니다.
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))
선택사항이 있는 경우 Scala 2.8을 참조하십시오.http://www.scala-lang.org/node/2075
Java 8의 람다를 사용하면 실제 명명된 파라미터에 더욱 근접할 수 있습니다.
foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
는 아마 의 "practices best practice")를일 수 .$□□□□□□□□★
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));
}
}
장점:
- 지금까지 본 어떤 건설업자 패턴보다 상당히 짧습니다
- 메서드와 컨스트럭터 모두에 대응
- 완전 타입 세이프
- 다른 프로그래밍 언어의 실제 명명된 파라미터에 매우 근접해 보입니다.
- 일반적인 빌더 패턴만큼 안전합니다(파라미터를 여러 번 설정할 수 있습니다).
단점:
- 당신 상사는 아마 이 일로 당신을 린치할 겁니다
- 무슨 일이 일어나고 있는지 알기가 더 어렵다
프로젝트 Lombok의 @Builder 주석을 사용하여 Java에서 명명된 파라미터를 시뮬레이션할 수 있습니다.그러면 모든 클래스의 새 인스턴스(작성된 클래스 및 외부 라이브러리에서 가져온 클래스 모두)를 만드는 데 사용할 수 있는 빌더가 생성됩니다.
클래스에서 이 기능을 유효하게 하는 방법은 다음과 같습니다.
@Getter
@Builder
public class User {
private final Long id;
private final String name;
}
그 후 다음 방법으로 사용할 수 있습니다.
User userInstance = User.builder()
.id(1L)
.name("joe")
.build();
라이브러리에서 나오는 클래스에 대해 이러한 Builder를 만들려면 다음과 같은 주석이 달린 정적 메서드를 만드십시오.
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();
입니다.Builder위의 로렌스가 설명한 패턴입니다.
나는 이것을 (적절한 장소에서) 자주 사용하고 있다.
주요 차이점은 이 경우 Builder는 부동이라는 것입니다.이것은 재사용이 가능하고 스레드 세이프하다는 장점이 있습니다.
이를 통해 하나의 기본 Builder를 만든 다음 필요한 다양한 위치에서 Builder를 구성하고 개체를 구축할 수 있습니다.
같은 오브젝트를 반복해서 빌드할 경우 빌더를 정적으로 만들 수 있기 때문에 설정을 변경할 필요가 없기 때문에 이 방법이 가장 적합합니다.
하는 하지 않습니다스태틱과 커스텀의 「다이나믹 생성」을 할 수 ).buildmethods를 합니다.
다음은 코드 예시입니다.
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의 어떤 솔루션도 매우 장황하지만 Google AutoValues나 Imutables와 같은 툴은 JDK 컴파일 시간 주석 처리를 사용하여 자동으로 빌더 클래스를 생성할 것입니다.
제 경우 Java 열거형에서 사용할 명명된 매개 변수를 원했습니다. 따라서 열거형 인스턴스는 다른 클래스에서 인스턴스화할 수 없기 때문에 빌더 패턴이 작동하지 않습니다.나는 @deamon의 답변과 비슷한 접근법을 생각해냈지만 파라미터 순서의 컴파일 타임 체크를 추가한다(더 많은 코드를 희생한다).
클라이언트 코드는 다음과 같습니다.
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; }
}
컴파일러 체크된 Builder 패턴을 다음에 나타냅니다.경고:
- 이것은 논쟁의 이중 할당을 막을 수 없다
- 있을 수 없다
.build() - 필드당 1개의 범용 파라미터
그래서 당신은 합격하지 못하면 실패할 수 있는 교실 밖의 무언가가 필요하다.Builder<Yes, Yes, Yes>를 참조해 주세요.getSum예를 들어 static 메서드입니다.
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);
}
}
경고 설명빌드 방법이 없는 이유문제는 그게 그 안에 있을 거라는 거예요.Builderclass, 그리고 parameterized가 됩니다.K1, K2, K3메서드 자체가 컴파일해야 하기 때문에 호출하는 모든 것이 컴파일되어야 합니다.그래서 보통 수업 자체의 방식으로는 컴파일 테스트를 할 수 없습니다.
비슷한 이유로 빌더 모델을 사용하여 이중 할당을 방지할 수 없습니다.
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");
}
}
다음 패턴을 적용한 이름 있는 파라미터를 모방할 수 있습니다.
public static class CarParameters {
// to make it shorter getters and props are omitted
public ModelParameter setName(String name) {
this.name = name;
return new ModelParameter();
}
public class ModelParameter {
public PriceParameter setModel(String model) {
CarParameters.this.model = model;
return new PriceParameter();
}
}
public class PriceParameter {
public YearParameter setPrice(double price) {
CarParameters.this.price = price;
return new YearParameter();
}
}
public class YearParameter {
public ColorParameter setYear(int year) {
CarParameters.this.year = year;
return new ColorParameter();
}
}
public class ColorParameter {
public CarParameters setColor(Color color) {
CarParameters.this.color = color;
return new CarParameters();
}
}
}
다음으로 다음과 같이 메서드에 전달할 수 있습니다.
factory.create(new CarParameters()
.setName("Ford")
.setModel("Focus")
.setPrice(20000)
.setYear(2011)
.setColor(BLUE));
자세한 내용은 https://medium.com/@ivorobioff/named-parameters-in-fc8c를 참조하십시오.
이제 모두 Java 17 ;-)을 사용하고 있기 때문에 레코드를 사용하는 것은 이 관용어를 흉내내는 매우 쉬운 방법입니다.
public class OrderTemplate() {
private int tradeSize, limitDistance, backoffDistance;
public record TradeSize( int value ) {}
public record LimitDistance( int value ) {}
public record BackoffDistance( int value ) {}
public OrderTemplate( TradeSize t, LimitDistance d, BackoffDistance b ) {
this.tradeSize = t.value();
this.limitDistance = d.value();
this.backoffDistance = b.value();
}
}
그 후, 다음과 같이 전화할 수 있습니다.
var t = new OrderTemplate( new TradeSize(30), new LimitDistance(182), new BackoffDistance(85) );
읽기 쉬워서 모든 int 파라미터가 혼재하지 않게 되었습니다.('사이즈가 처음인가, 거리가 처음인가').").
package org.xxx.lang;
/**
* A hack to work around the fact that java does not support
* named parameters in function calls.
*
* Its easy to swap a few String parameters, for example.
* Some IDEs are better than others than showing the parameter names.
* This will enforce a compiler error on an inadvertent swap.
*
* @param <T>
*/
public class Datum<T> {
public final T v;
public Datum(T v) {
this.v = v;
}
public T v() {
return v;
}
public T value() {
return v;
}
public String toString() {
return v.toString();
}
}
예
class Catalog extends Datum<String> {
public Catalog(String v) {
super(v);
}
}
class Schema extends Datum<String> {
public Schema(String v) {
super(v);
}
}
class Meta {
public void getTables(String catalog, String schema, String tablePattern) {
// pseudo DatabaseMetaData.getTables();
}
}
class MetaChecked {
public void getTables(Catalog catalog, Schema schema, String tablePattern) {
// pseudo DatabaseMetaData.getTables();
}
}
@Test
public void test() {
Catalog c = new Catalog("test");
assertEquals("test",c.v);
assertEquals("test",c.v());
assertEquals("test",c.value());
String t = c.v;
assertEquals("test",t);
}
public void uncheckedExample() {
new Meta().getTables("schema","catalog","%");
new Meta().getTables("catalog","schema","%"); // ooops
}
public void checkedExample() {
// new MetaChecked().getTables(new Schema("schema"),new Catalog("catalog"),"%"); // won't compile
new MetaChecked().getTables(new Catalog("catalog"), new Schema("schema"),"%");
}
@computable은 좋은 해결책을 생각해냈다.그러나 - 유효성 검사 및 일관성 검사가 수행되지 않으므로 클래스 인스턴스가 유효하지 않은 상태가 될 수 있습니다.따라서 빌더 클래스는 하위 클래스이지만 추가 하위 클래스는 생성하지 않고 빌더 솔루션과 결합하는 것이 좋습니다.또한 빌더 클래스가 추가될수록 상세해지기 때문에 람다를 사용하여 방법을 하나 더 추가했습니다.완성도를 높이기 위해 다른 구축자 접근 방식을 추가했습니다.
다음과 같은 클래스로 시작합니다.
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가 이미 게시한 것과 부분적으로 완전히 다른 것처럼 보이지만, 선택한 관습의 작은 차이를 알 수 있습니다.
만약 JLS가 명명된 파라미터를 실장할 수 있다면 어떻게 할 것인가?그들은 기존의 관용구 중 하나를 간략하게 지원함으로써 확장할 것인가?또한 Scala는 명명된 파라미터를 어떻게 지원합니까?
음 - 충분히 연구할 수 있고, 어쩌면 새로운 질문일 수도 있습니다.
언급URL : https://stackoverflow.com/questions/1988016/named-parameter-idiom-in-java
'programing' 카테고리의 다른 글
| HTML5 WebSockets보다 AJAX의 긴/짧은 폴링은 어떤 상황에서 선호됩니까? (0) | 2022.10.23 |
|---|---|
| MySQL Workbench에서 테이블 생성 스크립트를 가져오려면 어떻게 해야 합니까? (0) | 2022.10.23 |
| 클래스가 정의된 PHP 파일(실행 시) 찾기 (0) | 2022.10.23 |
| MySQL: 쿼리에서 열 이름 또는 별칭 가져오기 (0) | 2022.10.23 |
| Python에서 트리를 구현하려면 어떻게 해야 하나요? (0) | 2022.10.23 |