1

I am new to Java 8 and was trying to rewrite an existing code snippet logic using the Java 8 features.

However I am not sure how to use an existing arrayList defined outside the block to get values from it when it is placed inside the lambda block. It complains that it has to be final or effectively final.

I started with converting the inner traditional for loop and encountered the same issues with a counter variable which I was able to sort using AtomicInteger but am not sure how to do that for arrayList as well as I cannot also define the arrayList inside the lambda block since it has a dependency of an i variable that is present in the outer while loop.

Any help will be much appreciated !!! Thanks in advance.

Below is my code snippet :-


public String somemethod(ArrayList someValues){
        int i=0;
        String status="Failed";
        
        ArrayList someOtherValues = new ArrayList();
    try
    {
    while ( i < (someValues.size()))
        {
            someOtherValues = (ArrayList) someValues.get(i);
            someOtherValues.replaceAll(t -> Objects.isNull(t) ? "" : t); //converting every null to "" within the arrayList someOtherValues
            int count=4;
            AtomicInteger counter=new AtomicInteger(5);
            if(!someOtherValues.get(0).toString().equals(""))
            {
            while ( count < (someOtherValues.size()))
            {
                
                IntStream.range(0, 3).forEach(k -> {
                someObjectClass someObject=new someObjectClass();
                someOtherObjectClass id = new someOtherObjectClass(someOtherValues.get(0).toString(),someOtherValues.get(count).toString()) //Line where the error is
                someObject=new someObjectClass(id);
                 someObject.setId(id);
                 if(someCondition)
                 {
                     try
                     {
                     someObject.setSomething(someValue); 
                     counter.incrementAndGet()
                     }
                 }
                 someObject.setsomeOtherValues1(someOtherValues.get(1).toString());
                 someObject.setsomeOtherValues2(someOtherValues.get(3).toString())
                }
                 count=counter.get();
                 counter.incrementAndGet();

            }
            }
            i++;

    }
    catch(Exception e)
    {
        return status;
    }
             
        
}

Right now where it is pending is it complains that someOtherValues, which is an existing arrayList defined outside the lambda block needs to be final or effectively final in order to fetch elements.

Is it literally not possible to change/optimize the above function into a fewer lines of code using Java 8 streams/lambdas/forEach ?

7
  • Please provide code that compiles, yours doesn't. But yes if you define the list outside of the loop it's not final so it can't be used in lambdas. Define your list inside the loop. Commented May 11, 2021 at 11:27
  • The code obviously has a compile time error, since it is not allowing me to proceed as I have to declare the arrayList "someOtherValues" final but that is not possible. I cannot even declare it inside since it is being formed from someOtherValues = (ArrayList) someValues.get(i); and it is outside of the forEach block inside a while loop if you can see. I am looking for some help who can literally help me write the entire content in a java 8 way without logic being impacted. Thanks!!! Commented May 11, 2021 at 11:36
  • Hi. Could you please put the code that cause a error also can you explain what is you expect as result. Sometime you have to rewrite the method with stream rather than converting existing code Commented May 11, 2021 at 11:39
  • Line where the error is : ``` someOtherObjectClass id = new someOtherObjectClass(someOtherValues.get(0).toString(),someOtherValues.get(count).toString()) //Line where the error is ```. I want this method to be called from somewhere and get a status back which needs to be something other than failed. I cannot proceed because I cannot use an existing arraylist defined outside a lambda forEach block without declaring it final or making it as effectively final Commented May 11, 2021 at 11:53
  • Warning: you are using raw types (in your case ArrayList). Never use raw types, always provide the necessary type arguments. Commented May 11, 2021 at 13:57

1 Answer 1

0

As a general rule it is not a good idea to try and change outside variables inside a lambda definition. But since Java's very loose concept of final fields and variables only applies to assigning values, it can still be done. The only thing you cannot do in a lambda expression with variable defined outside is assigning new values. So this does not compile:

List<String> lst = new ArrayList<>();
myLambda = e -> {
  lst = new ArrayList<>(); //this violates the 'final' rule
  lst.add(e);
}

It is however possible to call any method of the outside variable, even if it changes the state of the variable. This will work:

myLambda = e -> {
  lst.add(e);
}

Even though you're still changed the state of the variable lst, this is legal code.

But I strongly advise against it. Lambdas are meant to used in a functional matter and should not change any variables defined elsewhere. It's a better choice to create a list inside the lambda, return it from the lambda and then add it to the outside list.

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

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.