-3

Comparing two Operator types used for adding a double to a double with: DoubleUnaryOperator and UnaryOperator<Double>:

    public void test() {
        double doub = 10.0;

        // This is OK
        DoubleUnaryOperator oo = d -> {
            return doub + d;
        };          
        // Compilation error: Local variable doub defined in an enclosing scope must be final or effectively final
        UnaryOperator<Double> o = d -> {
            return doub + d; 
        };

        doub = oo.applyAsDouble(.3);
        doub = o.apply(.3);
    }  

Why is there a compilation error (doub is not final) only for the UnaryOperator<Double> ? Why are there different results if the variable declaration never changes?

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
fidudidu
  • 399
  • 3
  • 10
  • Unable to reproduce. The first one fails to compile in all Java versions, because the assignment to already-initialized `doub` variable makes it not effectively final. Which compiler are you using? – Andreas Sep 15 '19 at 00:32
  • The issue is precisely the compilation error, please pay attention to the question. The issue is for Java 8 (in Java 7 you don't have UnaryOperator don't try to compile in all previous Java versions because there won't exist the java.util.function package) – fidudidu Sep 15 '19 at 00:50
  • When I say *all* Java versions, I'm referring to version 8, 9, 10, 11, 12, i.e. all version were lambdas are supported, so I'm of course ***not*** referring to versions, 7, 6, 5, ... --- I did pay attention to the question, and the question says that the `DoubleUnaryOperator` lambda "is OK", which is **not true**, because it fails to compile. Changing the order of the statements don't change that. – Andreas Sep 15 '19 at 02:44
  • I've removed the functional programming tag, as this question has nothing to do with functional programming. In the future, please kindly read tag descriptions before adding them. Many people subscribe to particular tags, and are not very happy when their queue fills with questions unrelated to their interests. – Jared Smith Sep 15 '19 at 11:38

2 Answers2

0

In my case (Java12) I got error in both DoubleUnaryOperator and UnaryOperator.

The case in your situation is that you are reassigning the doub with oo function result and after that you are trying to use the doub in next operator.

I'm citing the: Difference between final and effectively final

Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.

So, what I understood after checks is:

If the variable is final then it can be always used in the lambda expressions because there is no possibility to reassign it (there is - reflections, but that's another topic).

The effectively final variable appears when the declared variable remains unchanged.

If you would assign the result of oo to newly declared variable then you should be able to use this variable in o.

I believe that you tried to achieve 10.6 value, here's the sample:

double doub = 10.0;
DoubleUnaryOperator oo = d -> {
    return doub + d;
};
double ahh = oo.applyAsDouble(.3);
UnaryOperator<Double> o = d -> {
    return ahh + d;
};
System.out.println(o.apply(.3)); //The o.apply(.3) can be assigned to new variable 

Any reassigning to doub or ahh results in compile error (Java11)

itwasntme
  • 1,442
  • 4
  • 21
  • 28
0

I cannot reproduce your statement that one compiles while the other does not. For me, neither compiles.

Information:java: Errors occurred while compiling module 'tuts' Information:javac 1.8.0_192 was used to compile java sources Information:9/14/2019 8:10 PM - Build completed with 1 error and 0 warnings in 2 s 849 ms C:\Users\Philip\Code\tuts\src\test\java\tuts\UnaryOperatorTest.java Error:(13, 60) java: local variables referenced from a lambda expression must be final or effectively final

The problem is that you are attempting to reassign to doub in the outer scope after creating a new scope within the lambda that uses doub.

The functionality of the lambda becomes non-deterministic if you can alter the value of doub outside the scope of the lambda. Therefore, values from the outer scope must be declared final or be 'effectively' final (meaning you follow the rules and don't attempt to reassign to them in the outer scope).

You can get both alternatives to work if you just assign the results to a different variable (result, in the following example):

import org.junit.Assert;
import org.junit.Test;
import java.util.function.DoubleUnaryOperator;
import java.util.function.UnaryOperator;

public class UnaryOperatorTest {

    @Test
    public void testDoubleUnaryOperator() {
        double doub = 10.0;
        DoubleUnaryOperator doubleUnaryOperator = d -> d + doub;
        double result = doubleUnaryOperator.applyAsDouble(0.3);
        Assert.assertEquals(10.3, result, Double.MIN_VALUE);
    }

    @Test
    public void testUnaryOperator() {
        double doub = 10.0;
        UnaryOperator<Double> unaryOperator = d -> d + doub;
        double result = unaryOperator.apply(0.3);
        Assert.assertEquals(10.3, result, Double.MIN_VALUE);
    }

}

(Edit) I have copied and pasted your code verbatim. Neither FunctionalInterface example compiles in Java 8, as you can see from the problematic line numbers (31 and 36): enter image description here

However, making doub effectively final by assigning to result instead allows the code to compile: enter image description here

Philip Wrage
  • 1,505
  • 1
  • 12
  • 23
  • The issue is clear: in one case it is failing but not in the other one, I'm questioning why are there different results if the variable doub never changes. – fidudidu Sep 15 '19 at 01:09
  • @fidudidu See edits with screen shots demonstrating that neither version compiles. – Philip Wrage Sep 15 '19 at 01:32