JDK8의 일반적인 함수식 인터페이스 상세 정보
Function 함수식 인터페이스
Function이 무엇인지 여기는 소개하지 않겠습니다.Function이 jdk8에서 제공하는 기본 인터페이스라는 것을 알아야 합니다.Function 이름에서 알 수 있듯이 Function 인터페이스는 함수 인터페이스를 가리킨다.그러면 수학에 비추어 함수의 해석을 할 수 있다. 함수는 매개 변수를 지정하여 결과를 되돌려주는 것을 가리킨다(일대일, 다대일의 비추는 관계)
Function 소스는 다음과 같습니다.
@FunctionalInterface
public interface Function {
R apply(T t);
default Function compose(Function super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default Function andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static Function identity() {
return t -> t;
}
}
원본 코드를 통해 알 수 있듯이 Function은 하나의 함수식 인터페이스이고 그 안에 추상적인 방법
apply()
만 있다. 이 방법도 이 함수가 구체적으로 어떤 조작을 하는지 설명하는 동시에 Function 인터페이스도 우리에게 두 가지 기본 방법과 정적 방법을 제공했다.Function으로 뭘 해줄 수 있어요?
다음은 간단한 예를 통해 Function 인터페이스를 어떻게 사용해서 우리를 위해 일을 하는지 보여 준다.
예: 우리는 데이터를 입력하고 이 데이터에 일련의 조작 출력을 해야 한다.
public class Demo2 {
public static void main(String[] args) {
//
System.out.println(method1(2,(Integer a) -> a*a));
// 10
System.out.println(method1(2,a->a+10));
}
public static Integer method1(Integer a,Function function){
return function.apply(a);
}
}
우리는 위의 코드를 자세히 분석한다. 우리는 하나의 방법을 정의하기만 하면 된다. 이 방법은 Function의 apply 방법을 호출했을 뿐이다. 그러나 방법이 정의될 때 Function 인터페이스에 있는 apply 방법의 구체적인 실현을 제시하지 않고 방법이 호출될 때 방법에 대해 실현함으로써 하나의 방법을 정의하여 우리에게 많은 다른 일을 할 수 있다.이것이 바로 함수식 인터페이스, 즉 함수 프로그래밍의 큰 장점으로 방법에서 행위를 전달하거나 되돌아오는 행위를 할 수 있다
위에서 이미 apply 방법에 대해 소개하였으니, 다음은 다음 두 가지 기본 방법의 작용을 소개하겠습니다.
Function의 compose 방법
default Function compose(Function super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
이 방법을 통해 우리는 이 방법이 Function 형식의 매개 변수를 수신하고 Function 형식의 결과를 되돌려주는 것을 발견할 수 있다.
이 코드를 자세히 읽어 보겠습니다: (V v) -> apply (before.apply (v).이 문장 코드의 실행 과정:
public class Demo2 {
public static void main(String[] args) {
Function function1 = value -> value*3;
Function function2 = value -> value*value;
System.out.println(method1(2,function1,function2));
}
public static Integer method1(int a, Function function1, Function function2){
return function1.compose(function2).apply(a);
}
}
//output:12
이 코드를 통해 우리는 이 코드가 출력한 결과가 12라는 것을 발견할 수 있다. 그러면 왜 12입니까?우리는 자세하게 연구하러 왔다.
function2 apply :
apply(value){
return value * value;
}
function1 apply :
apply(value){
return value * 3;
}
Function apply :
apply(value){
return function1.apply
}
compose 방법에 대해 왜 Function 형식을 입력하는 두 번째 범주는 호출자 Function 형식의 첫 번째 범주 형식의 하위 형식이어야 한다고 규정했는지 설명합니다.다음은 해답입니다.
public class Demo2 {
public static void main(String[] args) {
Function function1 = value -> (int)value;//T:Number R:Integer
Function function2 = value -> Integer.parseInt(value);//T2:String R2:Integer ====> R2 T
System.out.println(method1(2,function1,function2));
}
public static Number method1(int a, Function function1, Function function2){
return function1.compose(function2).apply("2");
}
}
이 프로그램을 통해 알 수 있듯이
function1.compose(function2).apply("2")
밑바닥은 먼저 실행function2.apply(2)
이기 때문에 function2.apply(2)
의 출력 형식 R은 function1.apply(function2.apply(2))
입력이고 공교롭게도 function2의 출력 형식 R은 function1의 입력 형식 T의 하위 형식이나 T 형식을 정의했다.Function의 andThen 방법
원본 분석을 통해 andThen 방법과 compose 방법은 상반된다.
default Function andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
BiFunction 함수 인터페이스
이 함수 인터페이스는 두 개의 매개 변수를 수신하여 최종 출력 조작 후의 결과이다
@FunctionalInterface
public interface BiFunction {
R apply(T t, U u);
default BiFunction andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
원본 코드를 통해 이 인터페이스는 추상적인 방법인 apply만 두 개의 매개 변수를 수신하고 하나의 값을 출력하는 것을 발견했다.또 하나의 기본 방법은 andThen 방법이다.자세히 살펴보면 왜 위의 Function 인터페이스와compose 방법이 없습니까?
위에서 설명한 바와 같이andThen 방법은 호출자의 apply 방법을 먼저 실행하고 수용자 파라미터의 apply 방법을 실행하는 것이다.compose 방법은 매개 변수의 apply 방법을 먼저 실행하고 호출자의 apply 방법을 실행합니다.BiFunction은 두 개의 매개 변수를 수신하기 때문에 호출자의 apply 방법을 먼저 실행해야만 두 개의 매개 변수를 수신할 수 있습니다.Function 형식의 매개 변수를 직접 실행하는 apply 방법은 두 개의 매개 변수를 받아들일 수 없습니다.따라서 BiFunction 방법 중에는 andThen 방법이 하나밖에 없다.
Consumer 인터페이스
소비자는 이름처럼 소비자임을 알 수 있다.즉, 이 인터페이스의 주요 기능은 데이터를 전송하고 출력하지 않는 것이다.
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
default Consumer<T> andThen(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
this.accept(var2);
var1.accept(var2);
};
}
}
위의 원본 코드 분석을 통해 알 수 있듯이 Consumer는 추상적인 방법이 하나 있는데 그것이 바로accept(이 방법은 사용자로 하여금 이 소비자가 전송된 데이터를 어떻게 소비하는지 정하게 하는 것이다)이고andThen 방법도 있다.
Consumer andThen 방법:
default Consumer andThen(Consumer super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
this.accept(var2);
var1.accept(var2);
};
}
이 원본 분석을 통해:andThen은 Consumer 유형의 매개 변수를 수신합니다.이 방법의 실행 원리에 대한 분석:
Supplier 인터페이스
먼저 소스 코드를 붙여넣습니다.
/**
* Represents a supplier of results.
*
* There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
*
This is a functional interface
* whose functional method is {@link #get()}.
*
* @param the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
위에서 제시한 원본 코드와 주석을 통해 우리는 Supplier 인터페이스와 Consumer가 상응하는 것을 명확하게 알 수 있다. 우리는 Supplier를 생산자로 삼을 수 있다.
Supplier
인터페이스의 추상적인 방법은 매개 변수를 받아들이지 않고 마지막에 우리에게 값을 되돌려주는 것이다.Supplier 예:
public class TestSuplier {
public static void main(String[] args) {
TestSuplier demo = new TestSuplier();
System.out.println(demo.createString(()->"hello world;"));
}
public String createString(Supplier supplier){
return supplier.get();
}
}
Predicate 인터페이스
같은 이치: 우리는 먼저 이 인터페이스의 원본 코드를 제시한다.
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* This is a functional interface
* whose functional method is {@link #test(Object)}.
*
* @param the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
*
Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
*
Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
상기 원본 코드에 대한 간단한 분석을 통해 우리는 이 인터페이스에 추상적인 방법이 있다는 것을 알 수 있다. 이 추상적인 방법은
test()
방법이다. 이 방법과 주석을 통해 알 수 있듯이 이 추상적인 방법은 하나의 값을 전달하고 하나의 boolean 값을 되돌려주는 데 사용된다. 이 방법의 가장 주요한 역할은 전송된 데이터에 대해 일련의 조작을 통해'여부'를 판단하는 조건으로 삼는 것이다.전송된 직책에 대한 판단은 사용자 스스로 정해야 한다.예:
public class TestPredicate {
public static void main(String[] args) {
TestPredicate demo = new TestPredicate();
System.out.println(demo.isOrNo(str->str.length()>5,"hello world;"));
}
public boolean isOrNo(Predicate predicate,String param){
return predicate.test(param);
}
}
이 예시 코드를 통해 우리는 테스트 방법의 작용이 무엇인지 잘 알 수 있다. (바로 사용자가 입력 파라미터에 대한 판단 방식을 확정하고 최종적으로 판단한 결과가 진실/거짓인지 아닌지)
and 방법 상세 설명
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
and 방법의 실현을 통해 우리는 and 방법이 우리가 자주 사용하는
&&
연산자 조작이라는 것을 잘 알 수 있다.일종의 단락 조작이다.예:public class TestPredicate {
public static void main(String[] args) {
TestPredicate demo = new TestPredicate();
System.out.println(demo.isOrNo(str->str.length()>5,str->str.length()<10,"hello world;"));
}
public boolean isOrNo(Predicate predicate,Predicate predicate2,String param){
return predicate.and(predicate2).test(param);
}
}
위의 방법은 문자열을 지정하여 이 문자열의 길이가 5보다 크고 10보다 작은지 판단하는 것이다.
방법
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
이 방법은 우리가 전통적으로 개발할 때 만났던
||
연산자이다.주어진 매개 변수에 대해 여러 가지 조건 판단을 하거나 연산하는 것이다.negate 방법
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate negate() {
return (t) -> !test(t);
}
이 방법은 입력한 매개 변수에 대해test 방법을 실시한 후의 결과를 반으로 하는 작업이다.
isEqual 방법
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
이 방법은
Predicate
중의 정적 방법으로 이 방법의 작용은 입력한 매개 변수에 대해 동일한 조작 여부를 판단하는 것이다.예:public class TestPredicate {
public static void main(String[] args) {
TestPredicate demo = new TestPredicate();
System.out.println(demo.isEquals(null,"hello"));
}
public boolean isEquals(String param1,String param2){
return Predicate.isEqual(param1).test(param2);
}
}