James Daugherty created GROOVY-11818:
----------------------------------------
Summary: For loop variable reference in anonymous classes yields
unexpected results
Key: GROOVY-11818
URL: https://issues.apache.org/jira/browse/GROOVY-11818
Project: Groovy
Issue Type: Bug
Affects Versions: 4.0.29, 3.0.25
Reporter: James Daugherty
I'm not sure if this is really a bug or design decision of Groovy that could be
improved.
Given this java code:
{code:java}
import java.util.*;
import java.util.stream.*;
class MyCommand {
String filename;
MyCommand(String filename) { this.filename = filename; }
String getFilename() { return filename; }
}
interface MyFile {
String getFilename();
}
List<MyCommand> commands = List.of(
new MyCommand("testing1"),
new MyCommand("testing2")
);
List<MyFile> files = new ArrayList<>();
for (MyCommand cmd : commands) {
MyFile file = new MyFile() {
@Override
public String getFilename() {
return cmd.getFilename();
}
};
files.add(file);
}
List<String> filenames = files.stream()
.map(MyFile::getFilename)
.collect(Collectors.toList());
filenames
{code}
You can execute this code in jshell, and it will output:
{code:java}
| created class MyCommand
| created interface MyFile
commands ==> [MyCommand@34c45dca, MyCommand@52cc8049]
files ==> []
filenames ==> [testing1, testing2]
filenames ==> [testing1, testing2] {code}
But the equivalent in groovy code:
{code:java}
class MyCommand {
String filename
}
interface MyFile {
String getFilename()
}
List<MyCommand> commands = [new MyCommand(filename: 'testing1'),new
MyCommand(filename: 'testing2')]
List<MyFile> files = []
for(MyCommand cmd in commands) {
MyFile file = new MyFile() {
String getFilename() {
cmd.filename
}
}
files.add(file)
}
files.collect{ it.filename }{code}
will output
{code:java}
[testing2, testing2] {code}
in Groovy 3.0.25 & 4.0.29 (tested in GroovyConsole). Adding a local variable
to store the filename for that loop will fix the groovy code to return the same
value as the Java equivalent. It seems the `cmd` variable is being reused on
each iteration instead of creating a new reference. This causes code inside of
the for loop for previous iterations to be updated to the new iteration value -
which is inconsistent with Java's behavior.
Note: using the closure iteration syntax (.each \{ }) does not have this same
behavior as Java. Since both Java & the closure iteration syntax produce the
same result, I think the for loop difference should be considered a bug in
Groovy.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)