5

This question is already asked. But today I found something odd. For the following code:-

public static List<EsbBucketInstanceDefinition> convertBucketDefinitionList(List<BucketInstanceDefinitionV1> bucketInstanceDefinitionV1List) {
    List<EsbBucketInstanceDefinition> response = new ArrayList<>();
    List<EsbBucketInstanceDefinition> finalResponse = new ArrayList<>();
    bucketInstanceDefinitionV1List.stream().forEach(e -> {
        EsbBucketInstanceDefinition esbBucketInstanceDefinition = new EsbBucketInstanceDefinition();
        esbBucketInstanceDefinition.setInstanceType(e.getInstanceType());
        esbBucketInstanceDefinition.setReportingGroup(e.getReportingGroup());
        esbBucketInstanceDefinition.setSliceVolume(e.getSliceVolume());
        esbBucketInstanceDefinition.setCounterName(e.getCounterName());
        esbBucketInstanceDefinition.setSubscriberGroupId(e.getSubscriberGroupId());
        // response.add(esbBucketInstanceDefinition); compiler error variable used in lambda should be final or effective final 
        finalResponse.add(esbBucketInstanceDefinition);
    });
    return finalResponse;
}

For this works fine. Looks like only variable name finalResponse is working. How and why? Is it valid to do?

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
masiboo
  • 4,537
  • 9
  • 75
  • 136
  • you are not modifying the reference itself, but adding to an `ArrayList` - this will work, but it is discouraged... – Eugene Jan 17 '19 at 15:24
  • also notice that in your case, you do not need `stream` at all; `List::forEach` exists – Eugene Jan 17 '19 at 15:26
  • Can u pls why is it discouraged? Pls, explain more. What is the recommended way? – masiboo Jan 17 '19 at 15:27
  • the recommended way it to do a `map(....).collect(Collectors.toList())`. search the documentation for side-effects – Eugene Jan 17 '19 at 15:28
  • 2
    You could possibly improve your code creating a constructor in `EsbBucketInstanceDefinition` and using `map` in streams with `collect` as: `public static List convertBucketDefinitionList(List bucketInstanceDefinitionV1List) { return bucketInstanceDefinitionV1List.stream() .map(e -> new EsbBucketInstanceDefinition(e.getInstanceType(), e.getReportingGroup(), e.getSliceVolume(), e.getCounterName(), e.getSubscriberGroupId())) .collect(Collectors.toList()); }` – Naman Jan 17 '19 at 15:48

1 Answers1

4

References may only be made to (effectively) final variables from within a lambda.

The reference held by finalResponse in effectively final, because it never changes. Note that changing the reference means assigning a new value to it, eg

finalResponse = someOtherList;

Changing the state of the object referred to (eg adding items to the list referred to by finalResponse) is irrelevant to what the value held by the variable finalResponse, ie

finalResponse.add(something);

Does not change the variable finalResponse; it only changes the object to which finalResponse refers.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    If this is the question to be inferred, how about closing [this as a duplicate](https://stackoverflow.com/questions/54238974/java-8-steam-variable-used-in-lambda-should-be-final-or-effective-final#comment95305866_54238974)? Since this doesn't still relate then to the commented out code in the question. At least, the suggestion using `map` and `collect` could have solved the problem by not making use of the lambda here at all. – Naman Jan 17 '19 at 16:59