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