developer tip

이 Java 8 람다가 컴파일에 실패하는 이유는 무엇입니까?

optionbox 2020. 9. 24. 07:46
반응형

이 Java 8 람다가 컴파일에 실패하는 이유는 무엇입니까?


다음 Java 코드는 컴파일에 실패합니다.

@FunctionalInterface
private interface BiConsumer<A, B> {
    void accept(A a, B b);
}

private static void takeBiConsumer(BiConsumer<String, String> bc) { }

public static void main(String[] args) {
    takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
    takeBiConsumer((String s1, String s2) -> "hi"); // Error
}

컴파일러는 다음을보고합니다.

Error:(31, 58) java: incompatible types: bad return type in lambda expression
    java.lang.String cannot be converted to void

이상한 점은 "OK"로 표시된 줄은 잘 컴파일되지만 "Error"로 표시된 줄은 실패한다는 것입니다. 본질적으로 동일 해 보입니다.


람다는와 일치해야합니다 BiConsumer<String, String>. JLS # 15.27.3 (Lambda 유형) 을 참조하는 경우 :

다음이 모두 참인 경우 람다 식은 함수 유형과 일치합니다.

  • [...]
  • 함수 형식의 결과가 void이면 람다 본문은 문 식 (§14.8) 또는 void 호환 블록입니다.

따라서 람다는 명령문 표현식이거나 void 호환 블록이어야합니다.

  • 생성자 호출은 명령문 표현식 이므로 컴파일됩니다.
  • 문자열 리터럴은 명령문 표현식이 아니며 무효 호환이되지 않으므로 ( 15.27.2의 예제 참조 ) 컴파일되지 않습니다.

기본적으로 new String("hi")는 실제로 무언가를 수행하는 실행 가능한 코드 조각입니다 (새 문자열을 생성 한 다음 반환합니다). 반환 된 값은 무시할 수 있으며 new String("hi")새 문자열을 만들기 위해 void-return 람다에서 계속 사용할 수 있습니다.

그러나은 "hi"자체적으로 아무것도하지 않는 상수 일뿐입니다. 람다 본문에서 그것으로 할 수있는 유일한 합리적인 일은 그것을 반환 하는 것입니다. 그러나 람다 메서드는 반환 유형 String또는 을 가져야 하지만를 Object반환 void하므로 String cannot be casted to void오류가 발생합니다.


첫 번째 경우는 "특별한"메서드 (생성자)를 호출하고 실제로 생성 된 객체를 사용하지 않기 때문에 괜찮습니다. 더 명확하게하기 위해 람다에 선택적 중괄호를 넣겠습니다.

takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK
takeBiConsumer((String s1, String s2) -> {"hi"}); // Error

그리고 더 명확하게, 나는 그것을 이전 표기법으로 번역 할 것입니다.

takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
    public void accept(String s, String s2) {
        new String("hi"); // OK
    }
});

takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
    public void accept(String s, String s2) {
        "hi"; // Here, the compiler will attempt to add a "return"
              // keyword before the "hi", but then it will fail
              // with "compiler error ... bla bla ...
              //  java.lang.String cannot be converted to void"
    }
});

첫 번째 경우 생성자를 실행하지만 생성 된 객체를 반환하지 않고 두 번째 경우에는 String 값을 반환하려고하지만 인터페이스의 메서드 BiConsumer가 void를 반환하므로 컴파일러 오류가 발생합니다.


JLS는 다음을 지정합니다.

함수 형식의 결과가 void이면 람다 본문은 문 식 (§14.8) 또는 void 호환 블록입니다.

이제 자세히 살펴 보겠습니다.

귀하의 takeBiConsumer메서드가 void 유형이므로 람다 수신 new String("hi")은이를 다음과 같은 블록으로 해석합니다.

{
    new String("hi");
}

which is valid in a void, hence the first case compile.

However, in the case where the lambda is -> "hi", a block such as

{
    "hi";
}

is not valid syntax in java. Therefore the only thing to do with "hi" is to try and return it.

{
    return "hi";
}

which is not valid in a void and explain the error message

incompatible types: bad return type in lambda expression
    java.lang.String cannot be converted to void

For a better understanding, note that if you change the type of takeBiConsumer to a String, -> "hi" will be valid as it will simply try to directly return the string.


Note that at first I tought the error was caused by the lambda being in a wrong invocation context, so I'll share this possibility with the community :

JLS 15.27

It is a compile-time error if a lambda expression occurs in a program in someplace other than an assignment context (§5.2), an invocation context (§5.3), or a casting context (§5.5).

However in our case, we are in an invocation context which is correct.

참고URL : https://stackoverflow.com/questions/29262002/why-does-this-java-8-lambda-fail-to-compile

반응형