java - 功能和消费者限制

标签 java lambda java-8

我决定在我的方法中使用函数作为参数,但发现了相当不愉快的事情。这是一个例子:

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

相当困惑。甚至方法(anyActionvoidAction)也应该有不同的名称。

是否可以使用 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/

相关文章:

java - 为什么 Comparable 在 jdk 1.8 中没有声明为 @FunctionalInterface 尽管它是 FunctionalInterface 之一?

java-8 - 该方法 "setHeader"的圈复杂度为139,大于授权的10

java - 枚举到字符串的转换

java - 如何在 Junit 测试中覆盖 catch block ?

Java Swing 如何修复 JLabel 大小

amazon-web-services - 无法以编程方式调用 AWS lambda

java - 将大于 127 的值存储在字节数组中

amazon-web-services - AWS Firehose 数据转换并发限制

java - 具有 Lambda 表达式的本地类

Java 8-多项目构建中的字符串重复数据删除总数?