람다 식에 사용되는 변수는 최종적이거나 효과적으로 최종적이어야합니다

람다 식에 사용되는 변수는 최종적이거나 효과적으로 최종적이어야합니다

사용하려고하면 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();그것의 반환 값을 사용하지 않고 호출
  • 과제 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();
}   

답변

자바 8 에는 “Effectively final”변수라는 새로운 개념이 있습니다. 초기화 후에도 값이 변하지 않는 비 최종 로컬 변수를 “Effectively Final”이라고합니다.

Java 8 이전 에는 익명 클래스 에서 비 최종 로컬 변수를 사용할 수 없었기 때문에이 개념이 도입되었습니다 . 익명 클래스 의 로컬 변수에 액세스하려는 경우 최종 로 만들어야합니다.

람다가 도입되었을 때이 제한이 완화되었습니다. 따라서 로컬 변수가 람다 자체로 초기화되면 변경되지 않으면 로컬 변수를 final로 만들어야 할 필요가 있습니다.

Java 8 은 개발자가 람다를 사용할 때마다 지역 변수 final을 선언하는 어려움을 인식 하고이 개념을 도입했으며 지역 변수를 최종적으로 만들 필요가 없었습니다. 따라서 익명 클래스에 대한 규칙이 변경되지 않은 경우에는 작성하지 않아도됩니다.final 람다를 사용할 때마다 키워드 .

여기서 좋은 설명을 찾았 습니다


답변

귀하의 예에서 forEach간단한 for루프 로 lamdba를 사용하여 변수를 자유롭게 수정할 수 있습니다. 또는 변수를 수정할 필요가 없도록 코드를 리팩터링하십시오. 그러나 오류의 의미와 오류 해결 방법에 대해 설명하겠습니다.

Java 8 언어 사양, §15.27.2 :

람다 식에서 선언되었지만 아직 사용되지 않은 로컬 변수, 형식 매개 변수 또는 예외 매개 변수는 final로 선언되거나 효과적으로 최종적이거나 ( §4.12.4 ), 사용을 시도 할 때 컴파일 타임 오류가 발생합니다.

기본적으로 calTz람다 (또는 로컬 / 익명 클래스) 내에서 로컬 변수 ( 이 경우)를 수정할 수 없습니다 . Java에서이를 달성하려면 가변 객체를 사용하고 람다에서 최종 변수를 통해 수정해야합니다. 여기에서 변경 가능한 객체의 한 예는 한 요소의 배열입니다.

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

답변

이런 종류의 문제에 대한 일반적인 해결 방법보다 변수를 수정할 필요가 없다면
람다를 사용하고 method-parameter에서 final 키워드를 사용하는 코드 부분 추출하는 입니다.


답변

람다 식에 사용 된 변수는 최종 변수이거나 효과적으로 최종 변수 여야하지만 마지막 요소 배열 하나에 값을 할당 할 수 있습니다.

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