Подтвердить что ты не робот

Поставщик Java 8 с аргументами в конструкторе

Почему поставщики поддерживают только конструкторы no-arg?

Если присутствует конструктор по умолчанию, я могу сделать это:

create(Foo::new)

Но если единственный конструктор принимает String, я должен сделать это:

create(() -> new Foo("hello"))
4b9b3361

Ответ 1

Это просто ограничение ссылочного синтаксиса метода - что вы не можете передать ни один из аргументов. Это как работает синтаксис.

Ответ 2

Но конструктор 1-arg для T, который принимает String, совместим с Function<String,T>:

Function<String, Foo> fooSupplier = Foo::new;

Какой конструктор выбран, рассматривается как проблема выбора перегрузки в зависимости от формы целевого типа.

Ответ 3

Если вам нравятся ссылки на методы, вы можете написать метод bind самостоятельно и использовать его:

public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
    return () -> fn.apply(val);
}

create(bind(Foo::new, "hello"));

Ответ 4

Почему поставщики работают только с конструкторами no-arg?

Поскольку конструктор 1-arg изоморфен интерфейсу SAM с 1 аргументом и 1 возвращаемым значением, например java.util.function.Function<T,R> R apply(T).

С другой стороны, Supplier<T> T get() изоморфен нулевому конструктору arg.

Они просто несовместимы. Либо ваш метод create() должен быть полиморфным, чтобы принимать различные функциональные интерфейсы и действовать по-разному в зависимости от того, какие аргументы поставляются, или вам нужно написать тело лямбда, чтобы действовать как код клея между двумя сигнатурами.

Каково ваше неудовлетворенное ожидание здесь? Что должно произойти по вашему мнению?

Ответ 5

Интерфейс Supplier<T> представляет собой функцию с сигнатурой () -> T, что означает, что она не принимает никаких параметров и возвращает что-то типа T. Ссылки методов, которые вы предоставляете в качестве аргументов, должны следовать этой сигнатуре, чтобы ее можно было передать.

Если вы хотите создать Supplier<Foo>, который работает с конструктором, вы можете использовать общий метод связывания, который предлагает @Tagir Valeev, или вы делаете более специализированный.

Если вы хотите, чтобы Supplier<Foo> всегда использовал эту "hello" String, вы могли бы определить ее один из двух разных способов: как метод или переменную Supplier<Foo>.

метод:

static Foo makeFoo() { return new Foo("hello"); }

переменная:

static Supplier<Foo> makeFoo = () -> new Foo("hello");

Вы можете передать метод с помощью ссылки метода (create(WhateverClassItIsOn::makeFoo);), и переменную можно передать просто с помощью имени create(WhateverClassItIsOn.makeFoo);.

Метод немного предпочтительнее, потому что его проще использовать вне контекста передачи в качестве ссылки на метод, а также его можно использовать в экземпляре, для которого требуется собственный специализированный функциональный интерфейс, который также () -> T или конкретно () -> Foo.

Если вы хотите использовать Supplier, который может принимать любую строку в качестве аргумента, вы должны использовать что-то вроде метода связывания @Tagir, упомянутого в обход необходимости поставлять Function:

Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }

Вы можете передать это как аргумент следующим образом: create(makeFooFromString("hello"));

Хотя, возможно, вам нужно изменить все вызовы "make..." на вызовы "поставлять...", чтобы сделать его немного понятным.

Ответ 6

Соедините Поставщика с функциональным интерфейсом.

Вот пример кода, который я собрал, чтобы продемонстрировать "привязку" ссылки на конструктор к конкретному конструктору с помощью функции, а также различные способы определения и вызова ссылок конструктора "factory".

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