일급 시민으로서 어떻게 기능하는지 알아보기 위해 Java 8을 가지고 놀고 있습니다. 다음 스 니펫이 있습니다.
package test;
import java.util.*;
import java.util.function.*;
public class Test {
public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
list.forEach(functionToBlock(myFunction));
}
public static void displayInt(Integer i) {
System.out.println(i);
}
public static void main(String[] args) {
List<Integer> theList = new ArrayList<>();
theList.add(1);
theList.add(2);
theList.add(3);
theList.add(4);
theList.add(5);
theList.add(6);
myForEach(theList, Test::displayInt);
}
}
내가하려고하는 것은 메소드 참조를 사용하여 메소드 displayInt
를 메소드에 전달하는 것 myForEach
입니다. 컴파일러에서 다음과 같은 오류가 발생합니다.
src/test/Test.java:9: error: cannot find symbol
list.forEach(functionToBlock(myFunction));
^
symbol: method functionToBlock(Function<Integer,Void>)
location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
myForEach(theList, Test::displayInt);
^
required: List<Integer>,Function<Integer,Void>
found: List<Integer>,Test::displayInt
reason: argument mismatch; bad return type in method reference
void cannot be converted to Void
컴파일러는 그것을 불평합니다 void cannot be converted to Void
. myForEach
코드가 컴파일되도록 서명에서 함수 인터페이스 유형을 지정하는 방법을 모르겠습니다 . 나는 단지의 반환 형식을 변경할 수 있습니다 알고 displayInt
에 Void
다음 반환 null
. 그러나 다른 곳으로 전달하려는 방법을 변경할 수없는 상황이있을 수 있습니다. 그대로 재사용하는 쉬운 방법 displayInt
이 있습니까?
답변
잘못된 인터페이스 유형을 사용하려고합니다. 이 경우 함수 유형 은 매개 변수를 수신하고 리턴 값을 가지므로 적합하지 않습니다. 대신 컨슈머 (이전 명칭 Block) 를 사용해야합니다
함수 타입은
interface Function<T,R> {
R apply(T t);
}
그러나 소비자 유형은 다음과 같은 제품과 호환됩니다.
interface Consumer<T> {
void accept(T t);
}
따라서 Consumer는 T를 받고 아무것도 반환하지 않는 (void) 메소드와 호환됩니다. 그리고 이것이 당신이 원하는 것입니다.
예를 들어, 목록에 모든 요소를 표시하려면 간단히 람다 식을 사용하여 소비자를 만들 수 있습니다.
List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );
위의 경우 람다 식은 매개 변수를 받고 반환 값이 없음을 알 수 있습니다.
이제이 유형의 소비를 만들기 위해 람다 식 대신 메소드 참조를 사용하려면 String을 수신하고 void를 반환하는 메소드가 필요합니다.
나는 방법 참조의 다른 유형을 사용하지만,이 경우 사용하여 객체 메소드 참조의의 포획을 이용 할 수 println
의 방법을 System.out
다음과 같이 객체 :
Consumer<String> block = System.out::println
아니면 단순히 할 수 있습니다
allJedi.forEach(System.out::println);
이 println
메소드는 accept
Consumer 의 메소드 와 마찬가지로 값을 수신하고 리턴 유형이 void이므로 적합합니다 .
따라서 코드에서 메소드 서명을 다음과 같이 변경해야합니다.
public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
list.forEach(myBlock);
}
그런 다음 정적 메소드 참조를 사용하여 다음과 같이 소비자를 작성할 수 있습니다.
myForEach(theList, Test::displayInt);
궁극적으로 myForEach
메소드를 완전히 제거 하고 간단하게 수행 할 수도 있습니다.
theList.forEach(Test::displayInt);
일등 시민으로서의 기능에 대하여
사실, 구조적 기능 유형이 언어에 추가되지 않기 때문에 Java 8은 일급 시민으로서 기능을하지 않을 것입니다. Java는 단순히 람다 식과 메소드 참조에서 기능적 인터페이스의 구현을 작성하는 대체 방법을 제공합니다. 궁극적으로 람다 식과 메서드 참조는 개체 참조에 바인딩되므로 우리가 가진 모든 것은 일류 시민으로서의 개체입니다. 중요한 것은 객체가 매개 변수로 전달되어 변수 참조에 바인딩되어 다른 메소드의 값으로 반환 될 수 있기 때문에 기능이 있다는 것입니다.
답변
인수를 취하지 않고 결과를 반환하지 않는 인수로 함수를 수락해야 할 때, 내 의견으로는 여전히 다음과 같은 것이 가장 좋습니다.
public interface Thunk { void apply(); }
코드 어딘가에. 함수형 프로그래밍 과정에서 ‘thunk’라는 단어는 이러한 기능을 설명하는 데 사용되었습니다. java.util.function에없는 이유는 내 이해를 초월합니다.
다른 경우에는 java.util.function에 원하는 서명과 일치하는 것이 있더라도 인터페이스의 이름이 내 코드의 함수 사용과 일치하지 않을 때 항상 올바른 느낌이 들지 않습니다. Thread 클래스와 관련된 용어 인 ‘Runnable’과 관련하여 여기에서 비슷한 점이 있다고 생각합니다. 따라서 서명이 필요할 수 있지만 독자를 혼란스럽게 할 가능성이 있습니다.
답변
로 설정 반환 형식 Void
대신 void
하고return null
// Modify existing method
public static Void displayInt(Integer i) {
System.out.println(i);
return null;
}
또는
// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});
답변
소비자 인터페이스 대신 소비자 인터페이스를 사용해야한다고 생각합니다. Function<T, R>
.
소비자는 기본적으로 값을 받아들이고 아무것도 반환하지 않도록 설계된 기능적인 인터페이스입니다 (예 : void).
귀하의 경우 다음과 같이 코드의 다른 곳에 소비자를 만들 수 있습니다.
Consumer<Integer> myFunction = x -> {
System.out.println("processing value: " + x);
.... do some more things with "x" which returns nothing...
}
그런 다음 myForEach
코드를 아래 스 니펫으로 바꿀 수 있습니다 .
public static void myForEach(List<Integer> list, Consumer<Integer> myFunction)
{
list.forEach(x->myFunction.accept(x));
}
myFunction을 일급 객체로 취급합니다.