Programming

람다 식에 사용되는 변수는 최종적이거나 사실상 최종적이어야합니다.

procodes 2020. 8. 14. 21:02
반응형

람다 식에 사용되는 변수는 최종적이거나 사실상 최종적이어야합니다.


람다 식에 사용되는 변수는 최종적이거나 사실상 최종적이어야합니다.

사용하려고 할 calTz때이 오류가 표시됩니다.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
    try {
        cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
            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;
}

final변수 수단은 한 번만 인스턴스화 될 수있다. Java에서는 람다와 익명의 내부 클래스에서 최종 변수가 아닌 변수를 사용할 수 없습니다.

이전 for-each 루프를 사용하여 코드를 리팩터링 할 수 있습니다.

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;
}

이 코드의 일부를 이해하지 못하더라도 :

  • v.getTimeZoneId();반환 값을 사용하지 않고 a를 호출 합니다.
  • 할당 calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());calTz사용하면 원래 전달 된 내용을 수정하지 않고이 방법에서 사용하지 않습니다.
  • 당신은 항상 반환합니다 null. 왜 void반환 유형으로 설정하지 않습니까?

이 팁이 개선에 도움이되기를 바랍니다.


다른 답변이 요구 사항을 증명하지만 요구 사항이 존재 하는 이유를 설명하지 않습니다 .

JLS는 §15.27.2 에서 이유를 언급합니다 .

효과적인 최종 변수에 대한 제한은 동적으로 변경되는 지역 변수에 대한 액세스를 금지하며, 캡처시 동시성 문제가 발생할 가능성이 있습니다.

버그 위험을 낮추기 위해 그들은 캡처 된 변수가 절대 변하지 않도록하기로 결정했습니다.


람다에서 최종적이지 않은 것에 대한 참조를 얻을 수 없습니다. 변수를 보유하려면 람다 외부에서 최종 래퍼를 선언해야합니다.

이 래퍼로 최종 '참조'개체를 추가했습니다.

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();
}   

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 developer used lambda and introduced this concept and made it unnecessary to make local variables final. So if you see the rule for anonymous class still not changed, it’s just you don’t have to write final keyword every time when using lambdas.

I found a good explanation here


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];
}

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.

참고URL : https://stackoverflow.com/questions/34865383/variable-used-in-lambda-expression-should-be-final-or-effectively-final

반응형