[java] 생성자에 인수가있는 Java 8 공급자

공급 업체가 인수가없는 생성자 만 지원하는 이유는 무엇입니까?

기본 생성자가 있으면 다음과 같이 할 수 있습니다.

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

항상 가능합니다. 이것이 맥락에서 의미가 있는지 여부는 다릅니다.

위에서 설명한 것처럼 정적 메서드 참조 호출에는 함수 소비 (스트림) 메서드에서 예상하는 것과 일치하는 반환 / 파라미터 유형과 해당 메서드의 수가 필요합니다.