转换 Java 函数式接口

2024-03-10

一如既往,我浏览 JDK 8 源代码并发现了非常有趣的代码:

@Override
default void forEachRemaining(Consumer<? super Integer> action) {
    if (action instanceof IntConsumer) {
        forEachRemaining((IntConsumer) action);
    } 
}

问题是:如何Consumer<? super Integer>可能是一个的实例 IntConsumer?因为他们处于不同的层次结构。

我制作了类似的代码片段来测试投射:

public class InterfaceExample {
    public static void main(String[] args) {
        IntConsumer intConsumer = i -> { };
        Consumer<Integer> a = (Consumer<Integer>) intConsumer;

        a.accept(123);
    }
}

但它抛出ClassCastException:

Exception in thread "main" 
    java.lang.ClassCastException: 
       com.example.InterfaceExample$$Lambda$1/764977973 
     cannot be cast to 
       java.util.function.Consumer

您可以在以下位置找到此代码:java.util.Spliterator.OfInt#forEachRemaining(java.util.function.Consumer) https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfInt.html#forEachRemaining-java.util.function.Consumer-


我们看下面的代码,你就明白为什么了吗?

class IntegerConsumer implements Consumer<Integer>, IntConsumer {
   ...
}

任何类都可以实现多接口,其中之一是Consumer<Integer>也许实现另一个是IntConsumer。有时当我们想要适应时就会发生IntConsumer to Consumer<Integer>并保存其原始类型(IntConsumer),那么代码如下所示:

class IntConsumerAdapter implements Consumer<Integer>, IntConsumer {

    @Override
    public void accept(Integer value) {
        accept(value.intValue());
    }

    @Override
    public void accept(int value) {
        // todo
    }
}

Note: 这是使用类适配器设计模式 https://stackoverflow.com/questions/9978477/difference-between-object-adapter-pattern-and-class-adapter-pattern.

THEN您可以使用IntConsumerAdapter两者都作为Consumer<Integer> and IntConsumer, 例如:

Consumer<? extends Integer> consumer1 = new IntConsumerAdapter();
IntConsumer consumer2 = new IntConsumerAdapter();

Sink.OfInt是一个具体用法类适配器设计模式 https://stackoverflow.com/questions/9978477/difference-between-object-adapter-pattern-and-class-adapter-pattern在jdk-8中。的缺点Sink.OfInt#accept(Integer)显然 JVM 会抛出一个NullPointerException当它接受一个null值,所以这就是为什么Sink is package可见的。


189 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#189  interface OfInt extends Sink http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#Sink<Integer http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Integer.java#Integer>, IntConsumer http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/function/IntConsumer.java#IntConsumer {
190 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#190 @Override http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Override.java#Override
191 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#191 void accept(int value);
193 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#193 @Override http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Override.java#Override
194 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#194 default void accept(Integer http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Integer.java#Integer i) {
195 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#195 if (Tripwire.ENABLED)
196 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#196 Tripwire.trip http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Tripwire.java#Tripwire.trip%28java.lang.Class%2Cjava.lang.String%29(getClass http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Object.java#Object.getClass%28%29(), "{0} calling Sink.OfInt.accept(Integer)");
197 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#197 accept http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#Sink.OfInt.accept%28int%29(i.intValue http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Integer.java#Integer.intValue%28%29());
198 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#198 }
199 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/stream/Sink.java#199 }

我发现为什么需要投射Consumer<Integer> to an IntConsumer如果通过像这样的消费者IntConsumerAdapter?

原因之一是当我们使用Consumer接受一个int编译器需要将其自动装箱到Integer。并且在方法中accept(Integer)你需要拆箱Integer to an int手动。 换句话说,每个accept(Integer)执行 2 个额外的装箱/拆箱操作。它需要提高性能,所以在算法库中做了一些特殊的检查。

另一个原因是重用一段代码。的身体OfInt#forEachRemaining(消费者) https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfInt.html#forEachRemaining-java.util.function.Consumer-是一个很好的应用例子适配器设计模式 https://stackoverflow.com/questions/9978477/difference-between-object-adapter-pattern-and-class-adapter-pattern用于重复使用OfInt#forEachRenaming(IntConsumer) http://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfInt.html#forEachRemaining-java.util.function.IntConsumer-.

default void forEachRemaining(Consumer<? super Integer> action) {
    if (action instanceof IntConsumer) {
    //   action's implementation is an example of Class Adapter Design Pattern
    //                                   |
        forEachRemaining((IntConsumer) action);
    }
    else {
    //  method reference expression is an example of Object Adapter Design Pattern
    //                                        |
        forEachRemaining((IntConsumer) action::accept);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

转换 Java 函数式接口 的相关文章

随机推荐