1

Using java-8 now I turned some explicit declaration into a lambda expression and got a compiler error. So suspect it is a "bug" of the current java-8 release (b105).

The sample code defines two Function objects with and without using a lambda expression. Both relay on an Predicate which is used by those functions. While the traditional implementation works, the lambda version reports an error:

java: variable fileExists might not have been initialized

This is not totally wrong, but the predicate is relevant if the Function is used not if the function itself is created (since the explicit version works well). Should I report a bug (someone has a link?) or did I miss something?

public class FileOpener {

public FileOpener(Predicate<File> fileExists) {
    this.fileExists = fileExists;
}

final Predicate<File> fileExists;

final Function<File, FileInputStream> openLambda = file -> {
    try {
        return fileExists.test(file) ? new FileInputStream(file) : null;
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    }
};

// this version compiles
final Function<File, FileInputStream> openFunction = new Function<File, FileInputStream>() {
    @Override
    public FileInputStream apply(File file) {
        try {
            return fileExists.test(file) ? new FileInputStream(file) : null;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
};

}

7
  • Before reporting a bug I would (a) upgrade to the latest version and (b) ask on the dedicated mailing list, maybe this one: mail.openjdk.java.net/mailman/listinfo/jdk8-dev The behaviour for the lambda seems reasonable because it is a capturing lambda and it probably needs to know about its parameter value when it is constructed. Commented Oct 15, 2013 at 14:57
  • And where is fileExists initialized? Aren't the fields initialized prior to the constructor? Commented Oct 15, 2013 at 22:02
  • @assylias: "capturing" only applies to local variables. Since it's not in a method, there are no local variables. Commented Oct 15, 2013 at 22:08
  • @Edwin: fileExists is initialized in the constructor and therefor AFTER openLambda. The question is if it is a serious problem as java8 reports. Commented Oct 16, 2013 at 8:43
  • @Ditz The thing is that openLambda is an instance field, therefore, during object creation, it is evaluated PRIOR to the constructor, thus fileExists is not initialized at that point. Commented Oct 16, 2013 at 12:52

2 Answers 2

1

The error message is correct as have been pointed out by Edwin Dalorzo. The initialization order is important here. First the field initializers are executed in the order of appearance in the source file, then the constructor. Lambdas referring variables capture the variables and in case of final variables (and all local variables) they capture the actual value of the variable which requires the variable to be initialized. This conforms to the Java language specification and has not changed in Java 8:

class SimpleTest
{
  SimpleTest()
  {
    first="a string";
  }
  final String first;
  String second=first;
}

shows exactly the same behavior in version prior to Java 8.

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

5 Comments

The remedy would be to initialize openLambda also in the constructor --- there is no reason not to except that it looks a bit clumsy.
That’s right. The alternative would be removing the final modifier. But I would always prefer keeping the final modifier and initialize in the constructor.
I don't think this answer is correct. Lambdas only "capture" local variables. Instance variables are accessed through the outer class reference, and are not captured.
@Holger: do you have any document that says lambdas can capture instance variables?
@user102008: You seem to mistake “capture” for “copying”. Lambdas capture member variables for sure, otherwise you couldn’t access them. But for non-final instance variables “capture” implies accessing them via a remembered this instance. However, whether lambdas copy the values of final variables or re-access them is an unimportant implementation specific detail; these variables must be definitely assigned on lambda creation and must not change afterwards.
0

I've tried your code and played with it a bit... Turns out, if you remove final modifier from fileExists, everything compiles and works fine, so maybe discussion in comments is not 100% accurate, fileExists can actually be initialized in constructor. Looks like compiler does not detect that initialization is actually happening (ummm?).

This might be a bug... The only thing for bugs I've found is http://bugreport.sun.com/bugreport/ (despite SUN in name, actually leads to Oracle site).

P.S.: I'm using b111 of October, 10.

Comments

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.