Java编程网

分享 Java Web 开发相关知识

Java中的仅接口编程

defaultJava接口中的方法与Monad模式一起使用,可以编写“仅接口”类。这些类没有单独的实现类,只有接口。好吧,当然,从技术上讲,他们有实现,但是该实现是一个内联的小型匿名类。

我将通过编写(不完整的)Maybemonad 实现(有时称为Optionalor )来演示该想法Option。该容器用于表示可能存在或缺失的值。例如Map.get(key)可以使用该容器表示查找结果。如果找到了Some值,则返回值,否则返回None(空容器)。

Java 8具有Optional实现这种行为的功能,因此我们可以查看它的API并实现类似的功能:

public interface Option<T> {
    <U> Option<U> map(final Function<T, U> mapper);

    <U> Option<U> flatMap(final Function<T, Option<U>> mapper);

    Option<T> ifPresent(final Consumer<T> consumer);

    Option<T> ifEmpty(final Runnable consumer);

    T otherwise(final T replacement);
}

如果尝试直接实现此接口,则应创建一个将保留值的类。然后,在每种方法中,我们将检查该值是否是null,如果是,则执行一个操作,否则执行另一个操作。看起来像代码的味道-重复逻辑。让我们将此逻辑提取到方法中,该方法将接受两个函数并根据Option状态(即,是否存在值)调用它们:

   <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper);

现在,我们可以使用此新map()方法表示所有剩余方法:

    default <U> Option<U> map(final Function<T, U> mapper) {
        return Option.option(map(mapper, () -> null));
    }

    default <U> Option<U> flatMap(final Function<T, Option<U>> mapper) {
        return map(mapper, Option::empty);
    }

    default Option<T> ifPresent(final Consumer<T> consumer) {
        map(v -> {consumer.accept(v); return null;}, () -> null);
        return this;
    }

    default Option<T> ifEmpty(final Runnable consumer) {
        map(v -> v, () -> { consumer.run(); return null;} );
        return this;
    }

    default T otherwise(final T replacement) {
        return map(v -> v, () -> replacement);
    }

现在,我们需要的是两种静态工厂方法-一种用于创建空容器,另一种用于具有值的容器。由于对于所有功能,我们只需要一种方法,因此可以使用匿名类:

    static <T> Option<T> option(final T value) {
        return (value == null) ? empty() : new Option<T>() {
            @Override
            public <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper) {
                return presentMapper.apply(value);
            }
        };
    }

    static <T> Option<T> empty() {
        return new Option<T>() {
            @Override
            public <U> U map(final Function<T, U> presentMapper, final Supplier<U> emptyMapper) {
                return emptyMapper.get();
            }
        };
    }

就这样,所有必需的功能都在接口内部实现。

有趣的是,整个代码中只有一个条件运算符。仅因为我们遵循Java约定才能将其识别null为缺失值,所以这是必要的这不是严格必需的,并且此实现可以保留null值,并且仍然能够区分当前值和缺失值。
其他一些有趣的发现:

  • 空实例实际上不包含任何值,没有偶数字段(与Java 8不同Optional)。我们可以优化实现并每次为空容器返回相同的实例。
  • 没有用于存储值的显式实例变量,它由Java编译器在创建非空实例时隐式存储。
  • 代码内部缺少分支应该有助于在运行时获得更好的性能。在这种情况下,增益可以忽略不计,但是该技术是通用的,可以在增益可能很大的情况下使用。
《Java中的仅接口编程》讨论

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注