공급 업체가 인수가없는 생성자 만 지원하는 이유는 무엇입니까?
기본 생성자가 있으면 다음과 같이 할 수 있습니다.
create(Foo::new)
그러나 유일한 생성자가 String을 취하는 경우 다음을 수행해야합니다.
create(() -> new Foo("hello"))
답변
이는 메소드 참조 구문의 한계 일 뿐이며 인수를 전달할 수 없습니다. 구문이 작동하는 방식입니다.
답변
그러나 a T
를 취하는 1-arg 생성자 String
는 다음과 호환됩니다 Function<String,T>
.
Function<String, Foo> fooSupplier = Foo::new;
선택된 생성자는 대상 유형의 모양에 따라 과부하 선택 문제로 처리됩니다.
답변
메서드 참조가 너무 마음에 들면 bind
직접 메서드를 작성하여 사용할 수 있습니다.
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
답변
Supplier<T>
인터페이스의 서명 기능을 나타내는 () -> T
이 매개 변수를 사용하지 않는 및 유형의 무언가를 반환 의미 T
. 인수로 제공하는 메서드 참조가 전달 되려면 해당 서명을 따라야합니다.
Supplier<Foo>
생성자와 함께 작동 하는을 만들려면 @Tagir Valeev가 제안하는 일반 바인딩 메서드를 사용하거나 더 전문화 된 것을 만들 수 있습니다.
Supplier<Foo>
항상 해당 "hello"
문자열을 사용 하는를 원하면 메서드 또는 Supplier<Foo>
변수 의 두 가지 방법 중 하나로 정의 할 수 있습니다.
방법:
static Foo makeFoo() { return new Foo("hello"); }
변하기 쉬운:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
reference ( create(WhateverClassItIsOn::makeFoo);
) 메서드를 사용하여 메서드를 전달할 수 있으며 변수는 단순히 name을 사용하여 전달할 수 있습니다 create(WhateverClassItIsOn.makeFoo);
.
사람도 자신의 전문 기능 인터페이스를 요구하는이 방법을 참조로 전달되는 컨텍스트 외부에서 사용하기 쉽기 때문에이 방법은 좀 더 바람직하고, 또한 인스턴스에서 사용 할 수있어 () -> T
이거나 () -> Foo
특별히을 .
당신이 사용하려는 경우 Supplier
인수로 문자열을 취할 수를, 당신은 공급의 필요성을 우회, @Tagir 언급 바인드 방법 같은 것을 사용해야합니다 Function
:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
다음과 같은 인수로 전달할 수 있습니다. create(makeFooFromString("hello"));
비록 조금 더 명확하게하기 위해 모든 “make …”호출을 “supply …”호출로 변경해야 할 수도 있습니다.
답변
공급 업체가 인수가없는 생성자 만 사용하는 이유는 무엇입니까?
1 인수 생성자는 java.util.function.Function<T,R>
‘s R apply(T)
.
반면 Supplier<T>
s T get()
는 인수가없는 생성자와 동형입니다.
그들은 단순히 호환되지 않습니다. 어느 당신의 create()
방법에 필요한 다양한 기능의 인터페이스를 수용하고있는 인수가 제공됩니다 또는 두 개의 서명 사이의 글루 코드 역할을 할 람다 몸을 쓸 필요가 따라 다르게 행동 할 다형성 수 있습니다.
여기에서 충족되지 않은 기대는 무엇입니까? 당신의 의견으로 는 어떻게 해야 합니까?
답변
공급자를 FunctionalInterface와 페어링합니다.
다음은 Function을 사용하여 생성자 참조를 특정 생성자에 “바인딩”하고 “팩토리”생성자 참조를 정의하고 호출하는 다양한 방법을 보여주기 위해 함께 만든 몇 가지 샘플 코드입니다.
import java.io.Serializable;
import java.util.Date;
import org.junit.Test;
public class FunctionalInterfaceConstructor {
@Test
public void testVarFactory() throws Exception {
DateVar dateVar = makeVar("D", "Date", DateVar::new);
dateVar.setValue(new Date());
System.out.println(dateVar);
DateVar dateTypedVar = makeTypedVar("D", "Date", new Date(), DateVar::new);
System.out.println(dateTypedVar);
TypedVarFactory<Date, DateVar> dateTypedFactory = DateVar::new;
System.out.println(dateTypedFactory.apply("D", "Date", new Date()));
BooleanVar booleanVar = makeVar("B", "Boolean", BooleanVar::new);
booleanVar.setValue(true);
System.out.println(booleanVar);
BooleanVar booleanTypedVar = makeTypedVar("B", "Boolean", true, BooleanVar::new);
System.out.println(booleanTypedVar);
TypedVarFactory<Boolean, BooleanVar> booleanTypedFactory = BooleanVar::new;
System.out.println(booleanTypedFactory.apply("B", "Boolean", true));
}
private <V extends Var<T>, T extends Serializable> V makeVar(final String name, final String displayName,
final VarFactory<V> varFactory) {
V var = varFactory.apply(name, displayName);
return var;
}
private <V extends Var<T>, T extends Serializable> V makeTypedVar(final String name, final String displayName, final T value,
final TypedVarFactory<T, V> varFactory) {
V var = varFactory.apply(name, displayName, value);
return var;
}
@FunctionalInterface
static interface VarFactory<R> {
// Don't need type variables for name and displayName because they are always String
R apply(String name, String displayName);
}
@FunctionalInterface
static interface TypedVarFactory<T extends Serializable, R extends Var<T>> {
R apply(String name, String displayName, T value);
}
static class Var<T extends Serializable> {
private String name;
private String displayName;
private T value;
public Var(final String name, final String displayName) {
this.name = name;
this.displayName = displayName;
}
public Var(final String name, final String displayName, final T value) {
this(name, displayName);
this.value = value;
}
public void setValue(final T value) {
this.value = value;
}
@Override
public String toString() {
return String.format("%s[name=%s, displayName=%s, value=%s]", getClass().getSimpleName(), this.name, this.displayName,
this.value);
}
}
static class DateVar extends Var<Date> {
public DateVar(final String name, final String displayName) {
super(name, displayName);
}
public DateVar(final String name, final String displayName, final Date value) {
super(name, displayName, value);
}
}
static class BooleanVar extends Var<Boolean> {
public BooleanVar(final String name, final String displayName) {
super(name, displayName);
}
public BooleanVar(final String name, final String displayName, final Boolean value) {
super(name, displayName, value);
}
}
}
답변
매개 변수화 된 Supplier
문제 에 대한 해결책을 찾을 때 위의 답변이 도움이되었고 제안 사항을 적용했습니다.
private static <T, R> Supplier<String> failedMessageSupplier(Function<String,String> fn, String msgPrefix, String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> fn.apply(msgString);
}
다음과 같이 호출됩니다.
failedMessageSupplier(String::new, msgPrefix, customMsg);
아직 풍부한 정적 함수 매개 변수에 만족하지 않고 더 자세히 알아보고 Function.identity () 를 사용하여 다음과 같은 결과를 얻었습니다.
private final static Supplier<String> failedMessageSupplier(final String msgPrefix, final String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> (String)Function.identity().apply(msgString);
};
이제 정적 함수 매개 변수없이 호출 :
failedMessageSupplier(msgPrefix, customMsg)
이후 Function.identity()
리턴 유형의 기능 Object
, 그래서 다음의 호출 수행 apply(msgString)
에 캐스팅 String
이 필요합니다 -이 공급되고 또는 어떤 종류, 적용을 ().
이 방법을 사용하면 여러 매개 변수, 동적 문자열 처리, 문자열 상수 접두사, 접미사 등을 사용할 수 있습니다.
identity를 사용하는 것은 이론적으로 String :: new보다 약간 우위를 가져야하며 항상 새 문자열을 생성합니다.
Jacob Zimmerman이 이미 지적했듯이 더 간단한 매개 변수화 된 형식은
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
항상 가능합니다. 이것이 맥락에서 의미가 있는지 여부는 다릅니다.
위에서 설명한 것처럼 정적 메서드 참조 호출에는 함수 소비 (스트림) 메서드에서 예상하는 것과 일치하는 반환 / 파라미터 유형과 해당 메서드의 수가 필요합니다.