Как определить бифункцию, используя поставщика в качестве резервного для создания некоторых объектов?

#java #function #lambda #generic-programming #supplier

#java #функция #лямбда #generic-программирование #поставщик

Вопрос:

Что не так с этим кодом? Мне нужно создать фабричный метод, который зависит от бифункции, чтобы выполнять свою работу.

Компилятор сказал мне:

 The method apply(Class<T>, Supplier<I>) 
in the type BiFunction<Class<T>,Supplier<I>,T> 
is not applicable for the arguments (Class<A>, B::new)
  

но B расширяет A, B::new создает поставщика для B …

 import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.function.BiFunction;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

public class BiFunctionTest {

    static interface A {

    }

    static class B implements A {

    }

    static interface C {

    }

    static class D implements C {

    }

    private A a;
    private C c;

    public static <T, I extends T> BiFunctionTest create(
            BiFunction<Class<T>, Supplier<I>, T> fn) {
        BiFunctionTest o = new BiFunctionTest();
        o.a = fn.apply(A.class, B::new);
        o.c = fn.apply(C.class, D::new);
        return o;
    }

    @Test
    public void testBiFunction() {
        BiFunctionTest o = BiFunctionTest.create((i, s) -> s.get());
        assertEquals(B.class, o.a.getClass());
        assertEquals(D.class, o.c.getClass());
    }

}

  

Ответ №1:

Дженерики должны точно совпадать, они не проверяются ковариантно / контравариантно, если явно не объявлены как таковые. So Supplier<B> не является Supplier<A> , но является Supplier<? extends A> .

Затем ваша подпись изменится на следующую:

  public static <T, I extends T> BiFunctionTest create(BiFunction<Class<? extends T>, Supplier<? extends I>, T> fn) {...
  

Но теперь для второй проблемы, тело вашей функции не является универсальным с точки зрения T того, что вы используете A.class и B::new . Здесь следует использовать общую подпись, если реализация действительно не зависит от этих типов A и B но это не тот случай.

Итак, удаление обобщений из подписи:

 public static BiFunctionTest create(BiFunction<Class<? extends A>, Supplier<? extends A>, A> fn) {...
  

у нас есть соответствующая подпись для тела, которое вы предоставили:

     BiFunctionTest o = new BiFunctionTest();
    o.a = fn.apply(A.class, B::new);
    return o;
  

Комментарии:

1. Обновил мой сокращенный код, потому что удаление дженериков из signature не решает мою проблему. Класс, указанный в качестве первого аргумента, всегда является классом интерфейсов с одной или несколькими реализациями. Поставщик создает резервную реализацию, если на самом деле экземпляра нет.

2. p.s. пробовал также « public static BiFunctionTest create( BiFunction<Class<T>, Поставщик<? расширяет T>, T> fn) { BiFunctionTest o = new BiFunctionTest(); o.a = fn.apply(A.class , B::new); o.c = fn.apply(C.class , D::new); return o; } «

Ответ №2:

  public static <T> BiFunctionTest create2(
        BiFunction<Class<T>, Supplier<T>, T> fn) {
    BiFunctionTest o = new BiFunctionTest();
    Supplier<A> u = B::new;
    o.a = fn.apply(A.class, u);
    Supplier<? extends C> u2 = D::new;
    o.c = fn.apply(C.class, u2);
    return o;
}

@Test
public void testBiFunction() {
    BiFunctionTest o = BiFunctionTest.create2((i, s) -> s.get());
    assertEquals(B.class, o.a.getClass());
    assertEquals(D.class, o.c.getClass());
}
  

производит

 The method apply(Class<T>, Supplier<T>) 
in the type BiFunction<Class<T>,Supplier<T>,T> 
is not applicable for the arguments (Class<BiFunctionTest.A>, Supplier<BiFunctionTest.A>)
  

и

 The method apply(Class<T>, Supplier<T>) 
in the type BiFunction<Class<T>,Supplier<T>,T> 
is not applicable for the arguments (Class<BiFunctionTest.C>, Supplier<capture#1-of ? extends BiFunctionTest.C>)
  

Ответ №3:

решена путем добавления дополнительного интерфейса, но требует кастинга .. :/

 
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.function.BiFunction;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

public class BiFunctionTest {

    static interface A {
    }

    static interface B extends A {
    }

    static class ObjectB implements B {
    }

    static interface C extends A {
    }

    static class ObjectC implements C {
    }

    private B b;
    private C c;

    public static BiFunctionTest create3(
            BiFunction<Class<? extends A>, Supplier<? extends A>, A> fn) {
        BiFunctionTest o = new BiFunctionTest();
        o.b = (B) fn.apply(B.class, ObjectB::new);
        o.c = (C) fn.apply(C.class, ObjectC::new);
        return o;
    }

    @Test
    public void testBiFunction() {
        BiFunctionTest o = BiFunctionTest.create3((i, s) -> s.get());
        assertEquals(ObjectB.class, o.b.getClass());
        assertEquals(ObjectC.class, o.c.getClass());
    }

}