2

I'm doing a series of streaming operations to flatten what's effectively a 2D array.

Arrays.stream(attributes)
    .map(Attribute::getCommand)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .flatMap((array) -> (Arrays.stream((String[]) array)))
    .toArray(String[]::new)

Where Attribute conforms to the following interface:

public interface Attribute<T> {
  Optional<String[]> getCommand();  
}

However, the final flatMap() call isn't operating as expected.

  • .flatMap((array) -> (Arrays.stream((String[]) array))) works just fine.

  • .flatMap((array) -> (Arrays.stream(array))) fails to compile with java: no suitable method found for stream(java.lang.Object).

  • .flatMap(Arrays::stream) fails to compile with java: incompatible types: cannot infer type-variable(s) T (argument mismatch; java.lang.Object cannot be converted to T[]).

It seems to me that the type should be inferred just fine though. IntelliJ agrees and marks the cast as redundant and shows no compile errors with any of the three implementations. Why does Java require this apparently redundant typecast?


I additionally tried the following minimalist implementation:

import java.util.Arrays;
import java.util.Optional;

public class Streaming {

  public static void main(String[] args) {
    Optional<String[]>[] myarray = new Optional[]{Optional.of(new String[]{"Hello", "world"}),
        Optional.empty(), Optional.of(new String[]{"Foo"})};
    System.out.println(Arrays.toString(Arrays.stream(myarray).filter(Optional::isPresent).map
        (Optional::get).flatMap(Arrays::stream).toArray(String[]::new)));
  }

}

And it works just fine with all three implementations, outputting the expected [Hello, world, Foo].


Edit:

This was marked as a duplicate of this question. I may be wrong, but it seems that there's a distinction since this the type is specified in a more explicit manner. Notably, IntelliJ agrees that the cast is necessary in the example provided on said post, but not for my code. If I am mistaken, please let me know.


Edit:

Per request, the declaration of attributes is Attribute[] attributes = new Attribute[]{...} where ... is a variety of implementations of Attribute.

JMY1000
  • 131
  • 2
  • 9
  • 1
    Java only infers one level of method calls. It doesn't infer the type of nested lambda expressions. – Peter Lawrey Jul 23 '18 at 19:24
  • 1
    @PeterLawrey Okay, I think I get it. So Java is able to infer the return type of `Arrays.stream(attributes).map(Attribute::getCommand).filter(Optional::isPresent)` as `Optional` since it has an explicit return type, but not further, yes? – JMY1000 Jul 23 '18 at 19:30
  • yes, I am not sure why there is this limitation but I guess it is the simplify type interference. – Peter Lawrey Jul 24 '18 at 06:13
  • @JMY1000 It can be a bit confusing for relatively new users, but duplicate does not necessarily mean duplicate *question*, but duplicate *answer*. I would trust the person that closed the question that you can find your answer in the 10+ highly detailed answers on both posts. – Félix Adriyel Gagnon-Grenier Jul 24 '18 at 19:29
  • @FélixGagnon-Grenier I'm aware of that, I'm fairly acquainted with SE. As you can see in the answer below and other comments, the pertinent bit isn't the same in the answer of the duplicate question. – JMY1000 Jul 24 '18 at 19:36
  • Then maybe @JohnKugelman can reopen the question? I'm really not a subject matter expert, but that would be valid ground to reopen. – Félix Adriyel Gagnon-Grenier Jul 24 '18 at 19:45
  • @FélixGagnon the -Grenier Potentially, it's also possible that I've misinterpreted it (in which case it'd be nice to get an explanation as to what I've misinterpreted.) The raw type question is closer, but I'm still not entirely sure how I should handle things for my particular use case. – JMY1000 Jul 24 '18 at 19:53

1 Answers1

2

Attribute is a generic class (I wonder why as T is not used).
If you have this error it means that you declared a raw type of that such as :

Attribute[] attributes = ...;

For a raw type, the return type of getCommand() that is declared as Optional<String[]> becomes just Optional.
Declare Attribute as a generic type, for example : Attribute<String>[] attributes = ...; and this should compile without the cast or just remove the parameterized type T if it is not required.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • @JMY1000 What is your `attributes` declaration? – Logan Jul 23 '18 at 19:08
  • Attribute is a generic class : `class Attribute` : look a the ``. And you seem to declare a raw type : `Attribute` and not a generic type : `Attribute`. – davidxxx Jul 23 '18 at 19:15
  • But `getCommand()` is declared with return type `Optional`, not `Optional` or something. Why does the type suddenly get ignored? – JMY1000 Jul 23 '18 at 19:23
  • 2
    read again my answer : `For a raw type, the return type of getCommand() that is declared as Optional becomes just Optional.` – davidxxx Jul 23 '18 at 19:27
  • @davidxxx Gotcha, thanks. I think I'm probably just missing something from the duplicate question, but why does this occur? – JMY1000 Jul 23 '18 at 19:32
  • Your question has probably a duplicate but a thing is sure : not the referenced duplicate. Why does this occur? Because it is the way which the raw types work and whatever you use lambdas or not. – davidxxx Jul 23 '18 at 19:36
  • I think that your question is a little misleading because we don't see the declared type for the array of Attributes that you stream. I think that you can delete it as it will probably not help others. – davidxxx Jul 23 '18 at 19:39