15

I have this code, but it gives me an error:

Type mismatch: cannot convert from int to Character

Stream.iterate('a', i -> i + 1).limit(26).forEach(System.out::println);

Although it is fine to write int i = 'a';

I know I can write it like this, but that seems like too much code for a simple task.

Stream.iterate('a', i -> (char)(i + 1)).limit(26).forEach(System.out::println);

Why is the Java type inference failing?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
fastcodejava
  • 39,895
  • 28
  • 133
  • 186

2 Answers2

30

The reason why i -> i + 1 does not compile is because you're attempting to implicitly convert an int to a Character which the compiler cannot do itself alone.

In other words, you can think of Stream.iterate('a', i -> i + 1) as:

Stream.iterate('a', (Character i) -> {
       int i1 = i + 1;  
       return i1; // not possible 
});

As you have noted, explicitly casting to char solves it:

Stream.iterate('a', i -> (char)(i + 1))...

Btw this is better done as:

IntStream.rangeClosed('a', 'z').forEach(c -> System.out.println((char)c));

This is better because:

  1. No boxing overhead thus more efficient
  2. if you were to stop at say letter h with the use of iterate you'd have to do more brain processing than just entering h as the upper bound with rangeClosed because you'd need to find the number to truncate the infinite stream upon.
  3. Along with the boxing iterate generates an infinite stream which in this specific case has more overhead than the finite one with rangeClosed. Further, it's far easier to run IntStream.rangeClosed in parallel, not that you want to in this specific case but it's something to keep in mind. here is some discussion on Generators as sources by Brian Goetz.

etc...

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • "iterate generates an infinite stream which again has more overhead than a finite one." why's that? – Alexander Dec 28 '18 at 06:11
  • @Alexander Firstly, I will improve my wording as it may not be the best to remove ambiguity. Thanks. I have also included a reference to a blog which might be of interest. – Ousmane D. Dec 28 '18 at 07:53
  • 2
    "an infinite stream which in this specific case has more overhead than the finite one with" that's better :) Good callout on the split performance of this. – Alexander Dec 29 '18 at 17:39
  • The link is broken. The page seems to have been (re)moved. – Thiyagu May 05 '19 at 03:55
18

How about just:

Stream.iterate('a', i -> ++i).limit(26).forEach(System.out::println);

i -> i + 1 does not work because i is a Character and i + 1 causes an implicit narrowing conversion (JLS 5.1.3), which is not allowed. You can explicitly cast it as was shown. However ++i works because (From JLS 15.15.1):

Before the addition, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored.

The ++ operator takes care of the narrowing conversion without us having to explicitly cast it

GBlodgett
  • 12,704
  • 4
  • 31
  • 45