Variable used in lambda expression should be final or effectively final

后端 未结 7 754
抹茶落季
抹茶落季 2020-11-22 07:54

Variable used in lambda expression should be final or effectively final

When I try to use calTz it is showing this error.

相关标签:
7条回答
  • 2020-11-22 08:28

    From a lambda, you can't get a reference to anything that isn't final. You need to declare a final wrapper from outside the lamda to hold your variable.

    I've added the final 'reference' object as this wrapper.

    private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
        final AtomicReference<TimeZone> reference = new AtomicReference<>();
    
        try {
           cal.getComponents().getComponents("VTIMEZONE").forEach(component->{
            VTimeZone v = (VTimeZone) component;
               v.getTimeZoneId();
               if(reference.get()==null) {
                   reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue()));
               }
               });
        } catch (Exception e) {
            //log.warn("Unable to determine ical timezone", e);
        }
        return reference.get();
    }   
    
    0 讨论(0)
  • 2020-11-22 08:28

    In your example, you can replace the forEach with lamdba with a simple for loop and modify any variable freely. Or, probably, refactor your code so that you don't need to modify any variables. However, I'll explain for completeness what does the error mean and how to work around it.

    Java 8 Language Specification, §15.27.2:

    Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

    Basically you cannot modify a local variable (calTz in this case) from within a lambda (or a local/anonymous class). To achieve that in Java, you have to use a mutable object and modify it (via a final variable) from the lambda. One example of a mutable object here would be an array of one element:

    private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
        TimeZone[] result = { null };
        try {
            cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
                ...
                result[0] = ...;
                ...
            }
        } catch (Exception e) {
            log.warn("Unable to determine ical timezone", e);
        }
        return result[0];
    }
    
    0 讨论(0)
  • 2020-11-22 08:29

    Java 8 has a new concept called “Effectively final” variable. It means that a non-final local variable whose value never changes after initialization is called “Effectively Final”.

    This concept was introduced because prior to Java 8, we could not use a non-final local variable in an anonymous class. If you wanna have access to a local variable in anonymous class, you have to make it final.

    When lambda was introduced, this restriction was eased. Hence to the need to make local variable final if it’s not changed once it is initialized as lambda in itself is nothing but an anonymous class.

    Java 8 realized the pain of declaring local variable final every time a developer used lambda, introduced this concept, and made it unnecessary to make local variables final. So if you see the rule for anonymous classes has not changed, it’s just you don’t have to write the final keyword every time when using lambdas.

    I found a good explanation here

    0 讨论(0)
  • 2020-11-22 08:36

    A final variable means that it can be instantiated only one time. in Java you can't use non-final variables in lambda as well as in anonymous inner classes.

    You can refactor your code with the old for-each loop:

    private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
        try {
            for(Component component : cal.getComponents().getComponents("VTIMEZONE")) {
            VTimeZone v = (VTimeZone) component;
               v.getTimeZoneId();
               if(calTz==null) {
                   calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
               }
            }
        } catch (Exception e) {
            log.warn("Unable to determine ical timezone", e);
        }
        return null;
    }
    

    Even if I don't get the sense of some pieces of this code:

    • you call a v.getTimeZoneId(); without using its return value
    • with the assignment calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); you don't modify the originally passed calTz and you don't use it in this method
    • You always return null, why don't you set void as return type?

    Hope also these tips helps you to improve.

    0 讨论(0)
  • 2020-11-22 08:40

    if it is not necessary to modify the variable than a general workaround for this kind of problem would be to extract the part of code which use lambda and use final keyword on method-parameter.

    0 讨论(0)
  • 2020-11-22 08:42

    A variable used in lambda expression should be a final or effectively final, but you can assign a value to a final one element array.

    private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
        try {
            TimeZone calTzLocal[] = new TimeZone[1];
            calTzLocal[0] = calTz;
            cal.getComponents().get("VTIMEZONE").forEach(component -> {
                TimeZone v = component;
                v.getTimeZoneId();
                if (calTzLocal[0] == null) {
                    calTzLocal[0] = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
                }
            });
        } catch (Exception e) {
            log.warn("Unable to determine ical timezone", e);
        }
        return null;
    }
    
    0 讨论(0)
提交回复
热议问题