Suppose I do this in jshell:
jshell> void printIsEven(int i) {
...> System.out.println(i % 2 == 0);
...> }
| created method printIsEven(int)
jshell> List<Integer> l = Arrays.asList(7,5,4,8,5,9);
l ==> [7, 5, 4, 8, 5, 9]
jshell> l.forEach(/* ??? */); // is it possible to use a method reference here?
In a normal program I could write l.forEach(this::printIsEven)
in a non-static context or l.forEach(MyClass::printIsEven)
in the static context of a class named MyClass
.
Using this::printIsEven
in jshell doesn't work because jshell executes statements in a static context, but you can't use a static method reference because there's no class name to prefix ::printIsEven
, and trying l.forEach(::printIsEven)
is just a syntax error.
You can create a new class for that:
Technically it is no longer a top-level function, but it achieves the desired effect.
Now, if you knew that and still want to reference top-level methods...
As far as I can tell, the "top-level class" that holds "state" for the shell is
jdk.jshell.JShell
, butjdk.jshell.JShell::printIsEven
results inError: invalid method reference
. And you already mentioned it's not possible to make top-level methods static (Modifier 'static' not permitted in top-level declarations, ignored
).After a quick look at the JEP, it seems intentional. And it actually mentions the "define-static-method-in-new-class" approach from above.
I'm guessing the top-level "class" needs special magic to be able to redefine methods & other top-level declarations, and the limitations might derive from the JVM's own limitations in its ability to redefine classes/methods at runtime. The source is interesting but I'm not able to derive a meaningful answer from that.
Edit: So, I kinda got carried away. This is your fault.
I still think it's not possible to obtain a method reference to a top-level method in jshell, but... my previous guess about the reasons why is probably wrong.
The following shows that in jshell, when you "redefine" a class, the old class is still there: the evaluation context just shifts some mappings to resolve further references to the new class definition.
So this little demo suggests it has nothing to do with JVM internals for class redefinition, because no such thing happens here.
Then I wanted to see the list of all loaded classes:
Ah, yes, Java 9 modules... dammit :)
Oh, well, that'll be all for today.