我决定在我的方法中使用函数作为参数,但发现了相当不愉快的事情。这是一个例子:
final Random random = new Random();
public interface Animal {
public void sleep();
}
public class Cat implements Animal {
public boolean isAffectionate() {
return random.nextBoolean();
}
public void meow() {
System.out.println("meow");
}
@Override
public void sleep() {
System.out.println("my sofa");
}
}
public class Dog implements Animal {
public boolean isAngry() {
return random.nextBoolean();
}
public void bark() {
System.out.println("woof-woof");
}
@Override
public void sleep() {
System.out.println("my carpet");
}
}
public boolean isOwnerAtHome() {
return random.nextBoolean();
}
public <T, A extends Animal> T anyAction(Class<A> clazz, Function<A, T> action)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
A animal;
animal = (A) clazz.getConstructors()[0].newInstance();
T t;
if (isOwnerAtHome()) {
t = action.apply(animal);
} else {
animal.sleep();
t = action.apply(animal);
}
return t;
}
我知道这段代码很糟糕,但问题不在于代码设计。假设我想继续使用 lambda 并进行类似的设计。
要查看猫的 Activity (但前提是它会产生某些东西 - 而不是 void
):
public boolean booleanCatActionToday()
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return anyAction(Cat.class, c -> c.isAffectionate());
}
要查看 void
Activity ,我必须使用以下解决方法:
public <A extends Animal> void voidAction(Class<A> clazz, Consumer<A> action)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Function<A, Boolean> function = animal -> {
action.accept(animal);
return true;
};
anyAction(clazz, function);
}
只有那时:
public void voidCatActionToday()
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
voidAction(Cat.class, c -> c.meow());
}
相当困惑。甚至方法(anyAction
和 voidAction
)也应该有不同的名称。
是否可以使用 lambda 表达式而不完全重写使其不那么困惑?
最佳答案
通常我们使用一些语义来区分返回结果的函数和不返回结果的函数,例如:
static void <A extends Animal> void action(
Class<A> type,
Consumer<? super A> theAction) {...}
static void <A extends Animal, T> query(
Class<A> type,
Function<? super A, ? extends T> theQuery) {...}
Animal.action(Cat.class, Cat::meow);
boolean result =
Animal.query(Cat.class, Cat::isAffectionate);
这是因为它们通常意味着不同的东西,所以没有理由将它们混为一谈。
如果你需要重用某些东西,那么是的,你需要做一些笨拙的事情:
Animal.query(type, a -> {
theAction.accept(a);
return null;
});
或者可能考虑以不同的方式分解它:
private static <A extends Animal> Optional<A> create(
Class<A> type) throws NoSuchMethodException, InstantiationException, IllegalAccessException {
A a = type.getConstructor().newInstance();
if (!a.isOwnerHome()) {
a.sleep();
}
return a;
}
// "action"
theAction.accept(create(type));
// "query"
return theQuery.apply(create(type));
一般来说,请记住,API 的内部结构可能并不漂亮或完美,其目标是从外部使用它。
假设 lambda 的设计方式是 Consumer<T>
以某种方式隐式转换为 Function<T, Void>
.这对于想要重用一些代码的 API 设计者来说会很方便,但它会为那些 API 的用户以无意义/不正确的方式使用它们开辟一条道路:
<T> void send(List<T> elements) {
elements.stream()
// using 'map' to log
// when 'peek' is designed for this purpose
.map(e -> Debug.log("sending " + e))
.forEach(this::send);
}
关于java - 功能和消费者限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30056039/