Lambda 식 및 일반 메서드


111

일반적인 인터페이스가 있다고 가정합니다.

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

그리고 방법 sort:

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

이 메서드를 호출하고 람다 식을 인수로 전달할 수 있습니다.

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

잘 작동합니다.

그러나 이제 인터페이스를 제네릭이 아니고 메서드를 제네릭으로 만들면 다음과 같습니다.

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

그런 다음 다음과 같이 호출하십시오.

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

컴파일되지 않습니다. 람다 식에서 다음과 같은 오류를 표시합니다.

"대상 방법이 일반적 임"

좋아,을 사용하여 컴파일하면 javac다음 오류가 표시됩니다.

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

이 오류 메시지에서 컴파일러가 유형 인수를 유추 할 수없는 것 같습니다. 그럴까요? 그렇다면 왜 이런 일이 발생합니까?

인터넷을 통해 다양한 방법으로 검색했습니다. 그런 다음 방법을 보여주는 이 JavaCodeGeeks 기사를 찾았 으므로 시도했습니다.

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

그 기사가 작동한다고 주장하는 것과는 달리 다시 작동하지 않습니다. 일부 초기 빌드에서 작동했을 가능성이 있습니다.

그래서 내 질문은 : 제네릭 메서드에 대한 람다 식을 만드는 방법이 있습니까? 메서드를 생성하여 메서드 참조를 사용하여이 작업을 수행 할 수 있습니다.

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

어떤 수업에서는라고 말하고 다음 SO과 같이 전달하십시오.

sort(list, SO::compare);

답변:


117

당신은 사용할 수 없습니다 람다 식을 A에 대한 기능적인 인터페이스 의 방법 경우, 기능적인 인터페이스는형식 매개 변수를 . JLS8의 섹션 §15.27.3을 참조하십시오 .

T 가 기능적 인터페이스 유형 (§9.8)이고 표현식이 [..] T의 함수 유형과 일치 하는 경우 람다 표현식은 대상 유형 T 와 호환 가능합니다 . [..] 람다 표현식은 합동입니다. 다음이 모두 참인 경우 함수 유형으로 :

  • 함수 유형에는 유형 매개 변수없습니다 .
  • [..]

47
그러나이 제한은 제네릭 메서드에 대한 메서드 참조 에는 적용되지 않습니다 . 일반 기능 인터페이스가있는 일반 메소드에 대한 메소드 참조를 사용할 수 있습니다.
Brian Goetz 2014 년

17
이 제한에 대한 타당한 이유가 있다고 확신합니다. 뭔데?
Sandro

6
@Sandro : 람다 식에 대한 형식 매개 변수를 선언하는 구문이 없습니다. 그리고 그러한 구문은 매우 복잡합니다. 파서는 여전히 다른 합법적 인 Java 구조와는 별도로 유형 매개 변수를 사용하여 그러한 람다 표현식을 말할 수 있어야합니다. 따라서 메서드 참조에 의존해야합니다. 대상 메서드 는 설정된 구문을 사용하여 형식 매개 변수를 선언 할 수 있습니다 .
Holger 2015

2
@Holger, 타입 매개 변수가 자동 추론 될 수있는 곳에서 컴파일러는 예를 들어 Set <?>를 선언하고 캡처 된 타입으로 타입 검사를 할 때처럼 capure 타입을 속일 수 있습니다. 물론, 본문에 유형 매개 변수로 제공하는 것이 불가능하지만, 필요하다면 메소드 참조를 사용하는 것이 좋은 대안입니다.
WorldSEnder

17

메서드 참조를 사용하여 인수를 전달하는 다른 방법을 찾았습니다.

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);

3

컴파일러에게 적절한 버전의 일반 비교기를 지정하십시오. (Comparator<String>)

그래서 대답은

sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));


2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparableMyComparable일반 아니다 (어떤 유형) 그래서 (MyComparable<String>)중 하나가 작동하지 않을 것
user85421

1
@CarlosHeuberger 코드를 어떻게 입력하고 있는지 모르겠지만 저에게는 잘 작동합니다. 이것이 제가 찾고 있던 것입니다.
Ivan Perales M.

@IvanPeralesM. 2 개월 후 ... 코드를 복사하여 붙여넣고 정렬 줄 위의 행을 복사하여 붙여 넣었습니다. 다음과 같습니다. ideone.com/YNwBbF ! 위의 코드를 정확히 입력 하셨나요? 사용 Compartor?
user85421 2018

아니요, 답변 뒤에있는 아이디어를 사용하여 함수를 캐스팅하여 컴파일에 어떤 유형이고 작동하는지 알려줍니다.
Ivan Perales M.

@IvanPeralesM. 그럼 내 "타이핑"에 무슨 문제가 있습니까? 게시 된 답변은 작동하지 않습니다.
user85421

0

당신은 다음과 같은 것을 의미합니까? :

<T,S>(T t, S s)->...

이 람다는 어떤 유형입니까? Java로 표현할 수 없으므로 함수 응용 프로그램에서이 표현식을 작성할 수 없으며 표현식을 구성 할 수 있어야합니다.

이 작업이 필요하려면 Java의 Rank2 유형 에 대한 지원이 필요합니다 .

메서드는 제네릭이 될 수 있지만 식으로 사용할 수 없습니다. 그러나 전달하기 전에 필요한 모든 제네릭 유형을 특수화하여 람다 표현식으로 줄일 수 있습니다.ClassName::<TypeName>methodName


1
"Of what type is this lambda? You couldn't express that in Java..."유형은 다른 람다와 마찬가지로 컨텍스트를 사용하여 유추됩니다. 람다의 유형은 람다 자체에서 명시 적으로 표현되지 않습니다.
Kröw
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.