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


85

다음 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"로 표시된 줄은 실패한다는 것입니다. 본질적으로 동일 해 보입니다.


5
기능적 인터페이스 메서드가 void를 반환하는 것이 오타입니까?
Nathan Hughes

6
@NathanHughes 아니요. 질문의 중심이되는 것으로 밝혀졌습니다. 수용된 답변을 참조하십시오.
브라이언 고든

내부 코드가 있어야 { }takeBiConsumer당신은 예를 들어 줄 수 내가 올바르게 읽으면 ..., ... 그리고 만약 그렇다면 bc클래스 / 인터페이스의 인스턴스 BiConsumer라는 방법이 포함되어야하므로, 및 accept인터페이스 서명과 일치하기를. .. .. 그리고 바로 그이면 accept... 그래서 무엇을해야한다는 방법이다 요구 정의 할 곳 (인터페이스를 구현하는 것을 예를 클래스) {}?? ... ... ... 감사
dsdsdsdsd

단일 메서드가있는 인터페이스는 Java 8에서 람다와 상호 교환 할 수 있습니다.이 경우 (String s1, String s2) -> "hi"BiConsumer <String, String>의 인스턴스입니다.
Brian Gordon

답변:


100

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

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

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

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

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

31
@BrianGordon 문자열 리터럴은 식 (정확히 말하자면 상수 식)이지만 문식이 아닙니다.
assylias

44

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

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


6
올바른 형식 용어는 Expression Statement입니다 . 인스턴스 생성 표현식은 표현식이 있거나 명령문이 필요한 두 위치에 모두 나타날 수 있지만 String리터럴은 명령문 컨텍스트 에서 사용할 수없는 표현식 입니다 .
Holger

2
허용 대답은 공식적으로 정확한있을 수 있습니다, 그러나 이것은 더 나은 설명입니다
edc65

3
@ edc65 : 그렇기 때문에이 답변도 찬성되었습니다. 규칙과 비 형식 직관적 인 설명의 논리는 참으로 도움을하지만, 모든 프로그래머는 공식적인 규칙의 결과인지 그 뒤에 경우 공식적인 규칙이 있다는 것을 알고 있어야합니다 수 없는 직관적으로 이해할 수는, 공식 규칙은 승리 여전히 것 . 예는 ()->x++동안, 법적 ()->(x++)기본적으로 정확히 같은 일을 ..., 그렇지
홀거

21

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

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를 반환하므로 컴파일러 오류가 발생합니다.


12

JLS는 다음을 지정합니다.

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

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

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

{
    new String("hi");
}

이것은 void에서 유효하므로 첫 번째 케이스가 컴파일됩니다.

그러나 람다가 -> "hi"인 경우 다음과 같은 블록

{
    "hi";
}

Java에서 유효한 구문이 아닙니다. 그러므로 "hi"로 할 수있는 유일한 일은 그것을 돌려 보내는 것입니다.

{
    return "hi";
}

무효로 유효하지 않으며 오류 메시지를 설명합니다.

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

더 나은 이해를 위해 유형을 takeBiConsumerString으로 변경 -> "hi"하면 단순히 문자열을 직접 반환하려고 시도하므로 유효합니다.


처음에는 람다가 잘못된 호출 컨텍스트에 있기 때문에 오류가 발생했음을 확인 했으므로이 가능성을 커뮤니티와 공유하겠습니다.

JLS 15.27

할당 컨텍스트 (§5.2), 호출 컨텍스트 (§5.3) 또는 캐스팅 컨텍스트 (§5.5)가 아닌 다른 위치의 프로그램에서 람다식이 발생하면 컴파일 타임 오류입니다.

그러나 우리의 경우 우리 는 올바른 호출 컨텍스트에 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.