Универсальный функциональный интерфейс, который принимает и возвращает тот же тип - PullRequest
0 голосов
/ 12 мая 2018

Я ищу функциональный интерфейс, который удовлетворяет следующим двум требованиям:

  • Должен принимать и возвращать один и тот же тип
  • Тип должен выводиться при вызове метода наFunctionalInterface

Если бы требование было только первым, я могу создать простой FunctionalInterface следующим образом:

@FunctionalInterface 
public interface MonoFunction<T> {
    T apply (T arg);
}

Но для этого потребуется указать тип при использованииинтерфейс.Но я хочу, чтобы тип был выведен.Что-то вроде приведенного ниже псевдокода:

class A {
    int a;
}

class B {
    int b;
}

public static void main (String[] args) {
    A a;
    B b;

    MonoFunction foo = (obj) -> {
        system.out.println (obj)
        return obj;
    };

    a = foo.apply (new A());
    b = foo.apply (new B());
}

Как мне добиться чего-то подобного?

Ответы [ 4 ]

0 голосов
/ 28 февраля 2019

Это возможно с использованием универсального метода в функциональном интерфейсе:

@FunctionalInterface
interface Identity {
    < T > T apply( T input );
}

К сожалению, такой интерфейс, как этот, не может быть реализован с использованием лямбда-функции.Вместо этого это должно быть сделано старым способом, используя класс, и наиболее кратко с анонимным классом:

Identity id = new Identity() {
    public < T > T apply( T anyInput ){
        // do something
        return anyInput;
    }
};

Это тогда работает для любого ввода:

class A {
    int a = 1;
}

String outputString = id.apply( "one" );
int outputInteger = id.apply( 1 );
A outputClass = id.apply( new A() );
0 голосов
/ 17 мая 2018

Используйте общий фабричный метод, возвращающий функцию:

static <T> UnaryOperator<T> getFooFunction() {
    return obj -> {
        System.out.println(obj);
        return obj;
    };
}

public static void main (String[] args) {
    A a;
    B b;

    UnaryOperator<A> fooA = getFooFunction();
    a = fooA.apply(new A());
    UnaryOperator<B> fooB = getFooFunction();
    b = fooB.apply(new B());
    System.out.println(fooA==(Object)fooB);
}

Обратите внимание, что getFooFunction() не только возвращает семантически одну и ту же функцию, учитывая текущую реализацию (HotSpot / OpenJDK), это даже будет тот же объект, что вы можете легко протестировать с помощью fooA==(Object)fooB, поэтому нет причин пожертвовать безопасностью типа Дженерика.

То же самое происходит, когда вы используете UnaryOperator.identity().

0 голосов
/ 17 мая 2018

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

Вы можетеВыполните MonoFunction реализацию следующим образом (небезопасно и обычно не рекомендуется):

public class MonoFunction {
    private UnaryOperator<Object> func;

    @SuppressWarnings("unchecked")
    public <T> MonoFunction(UnaryOperator<T> func) {
        this.func = (UnaryOperator<Object>) func;
    }

    @SuppressWarnings("unchecked")
    public <T> T apply(T obj) {
        return (T) func.apply(obj);
    }
}

Обратите внимание, что это , а не a @FunctionalInterface, поэтому вам придется поставить свою лямбдуВыражение внутри вызова new MonoFunction(...) выглядит следующим образом:

public class MonoFunctionTest {
    public static void main(String[] args) {
        A a;
        B b;

        MonoFunction foo = new MonoFunction((obj) -> {
            System.out.println(obj);
            return obj;
        });

        a = foo.apply(new A()); // toString in A
        b = foo.apply(new B()); // toString in B

        MonoFunction bad = new MonoFunction((A obj) -> {
            System.out.println(obj);
            return obj;
        });

        a = bad.apply(a); // toString in A
        b = bad.apply(b); // ClassCastException: B cannot be cast to A
    }
}

class A {
    public String toString() { return "toString in A"; }
}
class B {
    public String toString() { return "toString in B"; }
}

Я еще раз подчеркиваю, что это небезопасно, и, как показано, относительно легко получить ClassCastException.

0 голосов
/ 12 мая 2018

Вы можете использовать UnaryOperator<T>, но вы должны заранее определить, какой тип вы ожидаете.

UnaryOperator<A> foo = a -> {
    system.out.println(a);
    return a;
};

В противном случае просто приведите свой результатв тип переменной:

a = (A) foo.apply (new A());
b = (B) foo.apply (new B());
...