【问题标题】:Java API class having a method with a meaningless type wildcard具有无意义类型通配符的方法的 Java API 类
【发布时间】:2018-01-23 13:20:20
【问题描述】:

在 Java 8 中,java.util.Optional (javadoc) 类提供了"Maybe" MonadOption Type 的功能。

更直接:

public final class java.util.Optional extends Object

一个容器对象,它可能包含也可能不包含非空值。如果一个 值存在,isPresent() 将返回 true,get() 将返回 价值。

One of the methods 是:

可选 map(Function super T,? extends U> mapper)

如果存在值,则对其应用提供的映射函数,如果 结果是非空的,返回一个描述结果的 Optional。

问题就是为什么map()U 类型上使用类型通配符。

我对类型通配符的理解很简单:

? super T 指定从ObjectT(包括ObjectT)的子类路径给出的类集中的某个类型,联合在任何拉入的子接口路径上找到的接口集通过implements“作为一个侧面”。

? extends U 只是从扩展U (Uincluded) 的类集中指定某种类型。

所以可以写:

可选 map(Function super T,U> mapper)

不会丢失信息。

还是不行?

【问题讨论】:

标签: java generics


【解决方案1】:

不完全是。

Function<? super T, U>Function<? super T, ? extends U> 不同。

例如,即使我将Function<Object, String> 传递给该方法,我仍然可以获得Optional<CharSequence>。如果方法被定义为<U> Optional<U> map(Function<? super T, U> mapper),那么这是不可能的。

这是因为泛型是不变的:<T><? extends T> 不同。这是用 Java 语言实现的设计决策。

让我们看看Jon Skeet explains what would happen如果泛型不是不变的:

class Animal { }

class Dog extends Animal { }

class Cat extends Animal { }
public void ouch() {
    List<Dog> dogs = Arrays.asList(new Dog(), new Dog());
    List<Animal> animals;
    // This would be legal, right? Because a list of dogs is a list of animals.
    List<Animal> animals = dogs;
    // This would be legal, right? Because a cat could be added to a
    // list of animals, because a cat is an animal.
    animals.add(new Cat());
    // Unfortunately, we have a confused cat.
}

虽然我不完全确定您在 cmets 中的意思,但我会尝试详细说明。

如果您可以完全控制您提供的Function,那么无论方法的签名是Function&lt;? super T, U&gt; 还是Function&lt;? super T, ? extends U&gt;,您只需相应地调整您的Function。但该方法的作者可能希望该方法尽可能灵活,允许提供Function,其第二个参数也是U 的子类,而不仅仅是@ 987654341@ 本身。实际上,您将其下限从U 扩大到U 的某个子类型。

所以函数应该真的读作&lt;U&gt; Optional&lt;? the-most-general-but-fixed-supertype-of U&gt; map(Function&lt;? super T, U&gt; mapper),但这样表达会很尴尬。

我确实会很尴尬。此外,您提出的符号与map() 的实际方法签名之间存在差异,这涉及下限上限的含义。

阅读更多:

【讨论】:

  • Optional.map 签名是Producer Extends Consumer Super 原则的一个很好的例子
  • 让我考虑一下。 Invariant 意味着泛型类型不具有可比性或没有任何层次关系,对吧?因为那会打开一罐蠕虫或运行时类型的信息携带和检查。而使用通配符可以让我们在相关类型之间表达一个额外的约束。
  • 感觉这是一个意图问题。我们想表达以下内容:“map 是一个返回 Optional&lt;U&gt; 的函数,其中 U 是最通用的类​​型,在这里仍然有意义(例如 CharSequence,并且因为我们希望避免为例如),它接受一个mapper,我们只要求它返回U的某个子类型。关键是mappers可能已经存在,我们希望在为它们编码map()时容纳它们. 而不是以另一种方式思考:map() 已经存在,我们将给它的mappers 编码...
  • ...在这种情况下,限制性的 &lt;U&gt; Optional&lt;U&gt; map(Function&lt;? super T,U&gt; mapper) 就可以了。所以这个函数应该真的读作&lt;U&gt; Optional&lt;? the-most-general-but-fixed-supertype-of U&gt; map(Function&lt;? super T,U&gt; mapper),但这样表达会很尴尬。
  • 我会接受这个作为答案,因为它有帮助,虽然它不是我的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
  • 2019-03-29
  • 1970-01-01
  • 2019-01-11
相关资源
最近更新 更多