我如何实现一种方法来接受消费者< Optional< T>>那在T中是逆变的? [英] How can I implement a method that accepts a Consumer<Optional<T>> that is contravariant in T?

查看:160
本文介绍了我如何实现一种方法来接受消费者< Optional< T>>那在T中是逆变的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的示例中,我可以将 Consumer< Optional< Integer> 传递给 foo ,但不能消费与LT;可选<号>> 。另一方面,我可以将任何一种类型传递给 foo2 ,但是我不能从方法体中调用消费者的accept方法。有没有办法改变 foo 方法,以便它可以工作?我最初的直觉是尝试 void foo(Consumer< Result <?super T>> c),但这显然并不意味着我会假设。

  import java.util.Optional; 
import java.util.function.Consumer;

公开课测试< T> {

public void foo(消费者<可选< T>> c){
可选< T> t = null;
c.accept(t); //编译
}

public void foo2(Consumer< ;? extends Optional< ;? super T>> c){
可选< T> t = null;
c.accept(t); //不会编译
}

public static void bar(){
test< Integer> t = null;
消费者<可选<号码>> crn = null;
消费者<可选<整数>> cri = null;

t.foo(cri); //编译
t.foo(crn); //不会编译

t.foo2(cri); //编译
t.foo2(crn); //编译
}
}


解决方案

原因是从类型系统的角度来看, Optional 并不是特别的:我们知道可选只有一个提供者方法( Optional.get()),并且它没有使用方法(例如 Optional.set(T));但编译器不会。

所以,编译器不会让你传递可选< Integer> 需要一个可选< Number> 方法,以防万一您调用神话的 set 方法你传入了 Double 而不是整数



解决此问题的唯一方法是将可选< T> 更改为可选< S> ,其中 S 是超类型 T 。您可以通过以下两种方式之一来执行此操作:




  • 由于可选及其缺乏消费者方法;但你会得到一个未经检查的警告(由于可选的属性,这实际上可以压制,

  • 创建一个新的可选的是正确的类型 - 可能更纯粹,但是会创建新实例的运行时间开销。



为了在方法中写这样的东西,你必须把它写成一个静态方法(可能在 test class)中,但它可以在其他地方); Java的类型系统没有足够的表达能力来编写实例方法签名所需的约束:

  public static< T,S延伸T> void foo3(Consumer< Optional< T>> c,test< s> test){
可选< S> s = null;

@SuppressWarnings(unchecked)//安全,因为可选的属性。
可选< T> t =(可选的< T>)(可选的<>);

c.accept(t);

$ / code>

并且像这样调用(使用 cri , crn t 来自问题代码):

  foo3(cri,t); //编译
foo3(crn,t); //编译

Ideone demo


In the following sample, I can pass a Consumer<Optional<Integer> to foo, but not a Consumer<Optional<Number>>. On the other hand, I can pass either type to foo2, but then I can't call the accept method of the consumer from the method body. Is there a way to change the foo method so that this works? My initial intuition was to try void foo(Consumer<Result<? super T>> c) but that apparently doesn't mean what I would assume.

import java.util.Optional;
import java.util.function.Consumer;

public class test<T> {

    public void foo(Consumer<Optional<T>> c) {
        Optional<T> t = null;
        c.accept(t); // compiles
    }

    public void foo2(Consumer<? extends Optional<? super T>> c) {
        Optional<T> t = null;
        c.accept(t); // doesn't compile
    }

    public static void bar() {
        test<Integer> t = null;
        Consumer<Optional<Number>> crn = null;
        Consumer<Optional<Integer>> cri = null;

        t.foo(cri); // compiles
        t.foo(crn); // doesn't compile

        t.foo2(cri); // compiles
        t.foo2(crn); // compiles
    }
}

解决方案

The reason for this is that Optional isn't special from the point of view of the type system: we know that Optional only has a provider method (Optional.get()) and that it has no consumer methods (like Optional.set(T)); but the compiler doesn't.

So, the compiler it won't let you pass an Optional<Integer> where an Optional<Number> is required: it is preventing you from ever calling that mythical set method, in case you passed in a Double instead of an Integer.

The only way around this is to change the Optional<T> into an Optional<S>, where S is a supertype of T. You can do this by either:

  • Casting - which you know is safe, because of the immutability of Optional and its lack of consumer methods; but you get an unchecked warning (which is actually fine to suppress, because of the properties of Optional).
  • Creating a new Optional of the right type - maybe more pure, but has the runtime overhead of creating the new instance.

In order to write such a thing in a method, you would have to write it as a static method (probably in the test class, but it could be elsewhere); Java's type system isn't expressive enough to be able to write the required constraints on an instance method's signature:

public static <T, S extends T> void foo3(Consumer<Optional<T>> c, test<S> test) {
    Optional<S> s = null;

    @SuppressWarnings("unchecked")  // Safe because of properties of Optional.
    Optional<T> t = (Optional<T>) (Optional<?>) s;

    c.accept(t);
}

and invoke like this (using the values of cri, crn and t from the question code):

foo3(cri, t); // compiles
foo3(crn, t); // compiles

Ideone demo

这篇关于我如何实现一种方法来接受消费者&lt; Optional&lt; T&gt;&gt;那在T中是逆变的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆