简体   繁体   English

为什么这段代码只用一个冗余的 map() 编译?

[英]Why does this code only compile with a redundant map()?

I've come across an oddity I can't explain.我遇到了一个我无法解释的怪事。

The following code (a minimal example) won't compile:以下代码(一个最小示例)将无法编译:

class Test {
  interface I {}

  Optional<I> findOne(ArrayList<? extends I> list) {
    return list.stream()
        .findFirst();
  }
}

javac (version 11 at least) says: javac(至少版本 11)说:

error: incompatible types: Optional<CAP#1> cannot be converted to Optional<I>
        .findFirst();
                  ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends I from capture of ? extends I

However, I sort of randomly found that adding a seemingly redundant.map() call to the stream [ edit : it's actually added to the Optional returned by findFirst()] compiles fine:但是,我随机发现,将看似冗余的.map() 调用添加到 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ [编辑:它实际上已添加到由 findFirst()] 返回的 Optional 中编译得很好:

class Test {
  interface I {}

  Optional<I> findOne(ArrayList<? extends I> list) {
    return list.stream()
        .findFirst()
        .map(a -> a);
  }
}

I'm curious about what is happening there that adding the.map() call lets it compile.我很好奇那里发生了什么,添加 .map() 调用让它编译。

[I know that changing the method parameter to take an ArrayList<I> works, but that's not what I'm asking about.] [我知道将方法参数更改为采用ArrayList<I>是可行的,但这不是我要问的。]

Generics are invariant Generics 是不变的

The first does not compile because Java generics are invariant .第一个无法编译,因为 Java generics 是不变的。

An Optional<Dog> is completely different to an Optional<Animal> , hence Optional<...> with ... being the type your ? Optional<Dog>Optional<Animal>完全不同,因此Optional<...> with ...是 your ? captured is not compatible to Optional<I> .捕获的与Optional<I>不兼容。

To elaborate, consider the following example:为了详细说明,请考虑以下示例:

List<Dog> dogs = new ArrayList<>();

List<Animal> animals = dogs; // this does not compile, but pretend it would
animals.add(new Cat());

Dog dog = dogs.get(0); // should be safe, but its actually a cat!

Upcast向上转型

But with the map(a -> a) you have an implicit upcast of the type, like (Animal) dog which makes this actually an Optional<Animal> .但是使用map(a -> a)你有一个类型的隐式向上转换,比如(Animal) dog这使得它实际上是一个Optional<Animal> So Optional<I> instead of Optional<...> in your case.因此,在您的情况下, Optional<I>而不是Optional<...>

So your a -> a lambda, which looks innocent actually is a method taking Dog (or ... from ? in your case) and giving out an Animal (or I ):所以你a -> a lambda,它看起来很无辜,实际上是一种获取Dog (或...?在你的情况下)并发出Animal (或I )的方法:

a -> (I) a

Consider the following example考虑以下示例

Optional<Dog> dog = Optional.of(new Dog());

Optional<Animal> animal = dog.map(d -> d);

which demonstrates exactly your situation.这正好说明了你的情况。 Because of invariance you can not just assign it, you have to actively convert it.由于不变性,您不能只分配它,您必须主动转换它。 And because of context, Java can implicitly cast the d to Animal .并且由于上下文, Java 可以将d隐式转换为Animal So the code is equivalent to所以代码相当于

dog.map(d -> (Animal) d);

Note笔记

Note that the map method you call here is not the map from Stream but from Optional , which is the result of findFirst .请注意,您在此处调用的map方法不是来自mapStream而是来自Optional的 map ,这是findFirst的结果。 So its purpuse is to map from an Optional<X> to an Optional<Y> by applying the given transformation.因此,其目的是通过应用给定的转换,将 map 从Optional<X>Optional<Y>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM