10

Java disallows usage of final variable inside a supplier as it might not be initialized, yet prepending "(this)." to variable makes it compile and run fine.

Furthermore calling such supplier results in NullPointerException instead of compiler error if called before assigning the variable and runs as expected if called after.

Is this behaviour described somewhere?

I am using OpenJDK 1.8.0_151.

Example:

import java.util.function.Supplier;
class Example {
  final String str;

  Supplier<Integer> test1 = () -> str.length();        // DOES NOT COMPILE
  Supplier<Integer> test2 = () -> this.str.length();   // DOES NOT COMPILE
  Supplier<Integer> test3 = () -> (this.str).length(); // DOES NOT COMPILE
  Supplier<Integer> test4 = () -> (this).str.length(); // OK

  Example(String str) {
    System.out.println(test4.get()); // NullPointerException
    this.str = str;
    System.out.println(test4.get()); // OK
  }
}

---

javac Example.java

Example.java:7: error: variable str might not have been initialized
Supplier<Integer> test1 = () -> str.length();        // DOES NOT COMPILE
                                ^
Example.java:8: error: variable str might not have been initialized
Supplier<Integer> test2 = () -> this.str.length();   // DOES NOT COMPILE
                                    ^
Example.java:9: error: variable str might not have been initialized
Supplier<Integer> test3 = () -> (this.str).length(); // DOES NOT COMPILE
                                     ^
3 errors
kristjank
  • 103
  • 6
  • 3
    I get a compilation error on all 4 Suppliers. – Eran Mar 05 '18 at 20:09
  • The behaviour will be documented **somewhere** in here: https://docs.oracle.com/javase/specs/jls/se8/html/jls-16.html – Oliver Charlesworth Mar 05 '18 at 20:14
  • 1
    Honestly I think the lexer is getting tripped up. Every other scenario rightfully catches this issue at compile time, yet adding parens allows one to overlook that. – Makoto Mar 05 '18 at 20:22
  • @YCF_L - Semantically, it means the same as `this` in this context (in the same way that `3` and `(3)` mean the same thing). It appears that it's confusing the compiler's flow analysis here, though. – Oliver Charlesworth Mar 05 '18 at 20:44
  • yes this is correct I think I need coffee I'm sorry @OliverCharlesworth – Youcef LAIDANI Mar 05 '18 at 20:45
  • 1
    @Eran: You do? That's interesting. How are you compiling it? (I get no compilation error for `test4` on [Ideone](https://ideone.com/00w37y), which says it's using HotSpot 8u112.) – user2357112 Mar 05 '18 at 21:07

1 Answers1

6

From JLS version 9, chapter 16:

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1).

str is the simple name of a final field, and this.str is the simple name of the field qualified by this. (this).str doesn't fall under either of those cases ((this) doesn't count as "qualified by this"), so it doesn't count as an access.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Yep. Lexer dun goof'd on this one. Strange that parens would cause this to fall in that *narrow* case. – Makoto Mar 05 '18 at 20:48