-4

Attempting to compile the following code with java-1.21.0-openjdk-amd64 results in these errors

.../TransactionProfile.java:[60,22] variable emvData might already have been assigned
.../TransactionProfile.java:[61,21] variable emvData might already have been assigned
.../TransactionProfile.java:[63,3] variable emvData might not have been initialized
public abstract class TransactionProfile {

  protected DeviceContext context;
  protected PeripheralResponse response;
  protected final PeripheralMessage emvData;
  protected final transient PeripheralTags peripheralTags = PeripheralTags.getInstance();

  TransactionProfile(DeviceContext context, PeripheralResponse response) {
    this.context = context;
    this.response = response;

    if (response == null) {
      emvData = new PeripheralMessage(new LinkedHashMap<>());
    } else {
      response.get(peripheralTags.emvDataTag)
          .ifPresentOrElse(
              emv -> emvData = new PeripheralMessage(emv.getUniqueMapOfChildren()),
              () -> emvData = new PeripheralMessage(new LinkedHashMap<>()));
    }
  }

When the constructor is lambda-free it compiles fine

  TransactionProfile(DeviceContext context, PeripheralResponse response) {
    this.context = context;
    this.response = response;

    Tlv emv = null;
    if (response != null) {
      emv = response.get(peripheralTags.emvDataTag).orElse(null);
    }
    if (emv == null) {
      emvData = new PeripheralMessage(new LinkedHashMap<>());
    } else {
      emvData = new PeripheralMessage(emv.getUniqueMapOfChildren());
    }
  }

How is emvData "definitely assigned" in the second form and not the first? As far as I can see there is no alternative. https://docs.oracle.com/javase/specs/jls/se15/html/jls-16.html

9
  • 1
    Why not just use the (IMO) simpler to understand and easier to debug non-lambda version? Commented May 18, 2024 at 12:00
  • @JJF i might do that, and I might not, but I still wouldn't know why the compiler thought only one of these ctors definitely assigned the emvData property Commented May 18, 2024 at 12:14
  • 1
    Another option is to extract into a static method getEmvDataFromThing, if you think the lambda version is more readable (which I don't). Would require rewriting it a bit Commented May 18, 2024 at 12:28
  • the compiler does no run the code, it does not know that ifPresentOrElse() will only execute one of its arguments, it cannot know that the final emvData will not be assigned twice. Commented May 18, 2024 at 12:32
  • 1
    new PeripheralMessage(response.get(peripheralTags.emvDataTag).map(Tlv::getUniqueMapOfChildren).orElse(new LinkedHashMap<>()) (I am guessing since no minimal reproducible example was posted) Commented May 18, 2024 at 13:11

1 Answer 1

3

The intuitive answer is that the Java compiler does not understand the semantics of the ifPresentOrElse method. Specifically, it doesn't understand that one or the other of those two lambdas will be executed.

The technical answer is that the definite assignment rules in Chapter 16 of the JLS do not say that the variable >is< definitely assigned in your example.

You could probably formally prove that an assignment will occur, assuming that ifPresentOrElse conforms to the javadoc. However that is beside the point. Definite assignment is determined by applying the JLS rules. A conformant Java compiler is not allowed to be "smart" about it.

Sign up to request clarification or add additional context in comments.

2 Comments

One gets used to IDE inspections going the extra mile
Yea ... well ... what the JLS says is "the law". If an IDE advised you your code is OK it would be a "bad lawyer".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.