19.04.18 람다식
Back-End/Java 2019. 4. 18. 18:08-람다식-
람다식을 사용하는 이유는 자바 코드가 매우 간결해지고, 컬렉션의 요소를 필터링 하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있기
때문이다.
Runnable 인터페이스의 익명 구현 객체를 생성하는 코드
Runnable runnable = new Runnable(){
public void run(){........} //익명 구현 객체
}
익명 구현 객체를 람다식으로 표현한 코드
Runnable runnable = (타입 매개변수) -> {실행문.......}; <- 람다식
-> 기호는 타입 매개 변수를 이용해서 중괄호{실행문}블록을
실행한다는 뜻으로 이해하면 된다.
만약 실행문에 return문만 있으면 중괄호는 생략할 수 있다.
ex) (x,y) -> x + y
-익명 구현 객체-
-말 그대로 이름이 없는 객체.
-단독 생성이 불가능 해서 클래스를 상속하거나 인터페이스를 구현해야만 생성이 가능하다.
-익명 구현 객체를 사용하면 인터페이스를 구현한 클래스를 따로 만들 필요가 없게 되어
더 간결하게 인터페이스를 구현할 수 있다.
ㄴ형식 : new 클래스명() {내용} 이렇게 사용한다.
-타겟 타입-
인터페이스는 직접 객체화할 수 없기 때문에 구현 클래스가 필요한데, 람다식은
구현 클래스를 생성하고 객체화 한다.
람다식은 대입될 인터페이스의 종류에 따라 작성 방법이 달라지기 때문에 람다식이 대입될 인터페이스를
람다식의 타겟 타입이라고 한다.
-함수적 인터페이스-
람다식은 하나의 메소드만 정의하기때문에 두개 이상의 추상 메소드가 선언된 인터페이스는
람다식을 사용해 구현 객체를 생성할 수 없다.
하나의 추상 메소드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있는데, 이러한 인터페이스를
함수적 인터페이스라고 한다.
함수적 인터페이스를 작성할때 컴파일러가 두 개 이상의 추상 메소드가 선언되지 않도록 체킹해주는
기능이 있는데 인터페이스 선언시 @Functionallnterface 어노테이션을 붙이면 된다.
어노테이션은 두 개 이상의 추상 메소드가 선언되면 컴파일 오류를 발생시킨다.
-예제 및 출력 결과-
1
2
3
4
5
6
7
8 |
package com.hs.chap14;
public interface MyFunctionalInterface {
public void method();
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
package com.hs.chap14;
public class MyFunctionalInterfaceExample {
public static void main(String[] args) {
MyFunctionalInterface fi;
fi = () -> {
String str = "method call1";
System.out.println(str);
//람다식으로 fi필드에 저장될 method메소드의 실행문을 적어준다.
};
fi.method(); //fi의 메소드를 호출하면 위에서 람다식으로 작성한 실행문이 출력된다.
fi = () -> {
System.out.println("method call2");
};
fi.method();
fi = () -> System.out.println("method call3");
fi.method();
}
}
|
cs |
-예제 및 출력결과-
(매개변수가 있는 추상메소드 정의)
1
2
3
4
5
6
7
8 |
package com.hs.chap14;
public interface MyFunctionalInterface {
//매개 변수가 있는 추상 메소드 정의
public void method(int a);
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
package com.hs.chap14;
public class MyFunctionalInterfaceExample {
// 추상메소드 매개변수에 들어갈 값을 설정.
private static int a = 5;
public static void main(String[] args) {
MyFunctionalInterface fi;
// 람다식으로 fi필드에 저장될 method메소드의 실행문과 매개변수를 적어준다.
fi = (a) -> {
String str = "매개변수가 들어간 람다식 실행";
System.out.println(str);
System.out.println("나의 이름은" + a + "글자 입니다.");
};
fi.method(a); // fi의 메소드를 호출하면 위에서 람다식으로 작성한 실행문이 출력된다.
}
}
|
cs |
-예제 및 출력결과-
(매개변수와 리턴값이 있는 추상메소드 정의 및 호출)
1
2
3
4
5
6
7
8 |
package com.hs.chap14;
public interface MyFunctionalInterface {
//두개의 매개 변수가 있는 추상 메소드 정의, 리턴값 없음
public int method(int a, int b);
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
package com.hs.chap14;
public class MyFunctionalInterfaceExample {
public static void main(String[] args) {
MyFunctionalInterface fi;
// 람다식으로 fi필드에 저장될 method메소드의 매개변수로 a,b를 선언하고, a+b의 값을 리턴해서 fi필드에 저장한다.
fi = (a, b) -> {
int result = a + b;
return result;
};
System.out.println(fi.method(5, 10)); // fi의 method에 5,10을 주고 출력하면 method가 호출되서 두수를 더한값이 출력된다.
}
}
|
cs |
-추상 메소드-
매개변수와 리턴값이 없는 메소드
-클래스 멤버와 로컬변수를 람다식으로 사용-
일반적으로 익명 객체 내부에서 this는 익명 객체의 참조이지만, 람다식에서 this는 내부적으로 생성되는
익명 객체의 참조가 아니라 람다식을 실행한 객체의 참조이다.
-디폴트 메소드-
인터페이스에 신규 메소드를 모든 구현 클래스 수정 없이 추가하고자 할때 사용한다.
인터페이스에 디폴트 메소드를 추가하고 Override하고 싶은 클래스만 구현하여 실행할 수 있다.
디폴트 메소드를 가진 인터페이스를 상속받을 때 추상 메소드로 재선언이 가능하다.
-로컬 변수-
메소드 안에 선언한 변수이고, 매소드의 매개 변수 또는 로컬 변수를 사용하면 이 변수는 final 특성을
가져야 한다. 그렇기 때문에 람다식 내부 또는 외부에서 로컬변수를 변경할 수 없다.
하지만 메소드 블록 내부에서 사용하는 것은 가능하다.
(이유 : 변수의 값이 변하는 것을 원치 않아서 메소드 내부에 변수 선언을 하게 되는데 시스템에서 이러한
특성때문에 메소드 내부에 선언된 변수는 fianl 타입으로 자동적으로 인식되서 메소드 밖에서 변환
할수가 없게끔 되는것이다.)
-Consumer 함수적 인터페이스-
리턴값이 없는 accept() 메소드를 가지고 있다.
accept() 메소드는 단지 매개값을 소비하는 역할만 한다. 여기서 소비한다는 말은 사용만 할 뿐 리턴값이
없다는 뜻이다.
-Supplier 함수적 인터페이스-
매개 변수가 없고 리턴값이 있는 getXXX() 메소드를 가지고 있다.
실행 후 호출한 곳으로 데이터를 리턴(공급)하는 역할을 한다.
-Predicate 함수적 인터페이스-
매개 변수와 boolean 리턴값이 있는 testXXXX() 메소드를 가지고 있다.
이 메소드들은 매개값을 조사해서 true 또는 false를 리턴하는 역할을 한다.
-andThen()와 compose()디폴트 메소드-
andThen()와 compose()디폴트 메소드 두개의 함수적 인터페이스를 순차적으로 연결하고, 첫 번째 처리 결과를
두 번째 매개값으로 제공해서 최종 결과값을 얻을때 사용한다.
인터페이스AB = 인터페이스A.andThen(인터페이스B);
최종결과 = 인터페이스AB.method();
andThen() 메소드는 인터페이스 AB의 method()를 호출하면 우선 인터페이스A 부터 처리하고 결과를
인터페이스B의 매개값으로 제공한다. 인터페이스B는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴
compose() 메소드는 인터페이스AB의 method()를 호출하면 인터페이스 B부터 처리하고 결과를 인터페이스A의
매개값으로 제공한다. 인터페이스A는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴한다.
-Predicate 종류 함수적 인터페이스-
and() 메소드 : 두 Predicate가 모두 true를 리턴하면 최종적으로 true를 리턴하는 Predicate를 생성한다.
or() 메소드 : 두 Predicate 중 하나만 true를 리턴하더라도 최종적으로 true를 리턴하는 Predicate를 생성한다.
negate() 메소드 : 원래 Predicate의 결과가 true이면 false로, false면 true를 리턴하는 새로운 Predicate를 생성한다.
isEqual() 메소드 : test() 매개값인 sourceObject와 isEqual()의 매개값인 targetObject를 Objects 클래스의 equals()의
매개값으로 제공하고, equals(sourceObject,targetObject)의 리턴값을 얻어 새로운 Predicate<T>를
생성한다.
-예제 및 출력 결과-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
package com.hs.chap14;
import java.util.function.Predicate;
public class PredicateIsEqualExample {
public static void main(String[] args) {
Predicate<String> predicate;
predicate = Predicate.isEqual(null);
System.out.println("null과 null은"+predicate.test(null));
predicate = Predicate.isEqual("Java");
System.out.println("Java와 null은"+predicate.test(null));
}
}
|
cs |
-정적 메소드와 인스턴스 메소드 참조-
정적 메소드를 참조할 경우에는 클래스 이름 뒤에 : : 기호를 붙이고 정적 메소드 이름을 기입한다.
클래스 : : 메소드
인스턴스 메소드일 경우에는 먼저 객체를 생성한 다음 참조 변수 뒤에 : : 기호를 붙이고 인스턴스 메소드 이름을 기술하면 된다.
참조변수 : : 메소드
-예제 및 출력 결과-
1
2
3
4
5
6
7
8
9
10
11
12 |
package com.hs.chap14;
public class Calculator {
public static int staticMethod(int x, int y) {
return x + y;
}
public int instanceMethod(int x, int y) {
return x + y;
}
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
package com.hs.chap14;
import java.util.function.IntBinaryOperator;
public class MethodReferencesExample {
public static void main(String[] args) {
IntBinaryOperator operator;
// 정적 메소드 참조
operator = (x, y) -> Calculator.staticMethod(x, y); // 람다식 사용
System.out.println("결과1: " + operator.applyAsInt(1, 2)); // x,y값에 각각 1과 2를 주어서 더한값을 출력함.
operator = Calculator::staticMethod; // staticMethod(정적메소드)를 참조
System.out.println("결과2: " + operator.applyAsInt(3, 4)); // x,y값에 각각 3과 4를 주어서 더한값을 출력함.
// 인스턴트 메소드 참조
Calculator obj = new Calculator();
operator = (x, y) -> obj.instanceMethod(x, y); // 람다식 사용
System.out.println("결과3:" + operator.applyAsInt(5, 6)); // x,y값에 5와 6을 넣어서 더한값 출력
operator = obj::instanceMethod; // instanceMethod(인스턴스 메소드)를 참조
System.out.println("결과4:" + operator.applyAsInt(7, 8)); // x,y값에 7과 8을 넣어서 더한값 출력
operator = (x, y) -> obj.instanceMethod(x, y); // 람다식 사용
System.out.println("결과3:" + operator.applyAsInt(5, 6)); // x,y값에 5와 6을 넣어서 더한값 출력
}
}
|
cs |
-중요 예제-
http://www.java2s.com/Tutorials/Java/java.util.function/Consumer/index.htm
'Back-End > Java' 카테고리의 다른 글
19.04.20 스트림과 병렬 처리 (0) | 2019.04.20 |
---|---|
19.04.19 컬렉션 프레임워크 (0) | 2019.04.19 |
19.04.17 제네릭,와일드카드 (0) | 2019.04.17 |
19.04.16 멀티 스레드 (0) | 2019.04.16 |
19.04.15 String클래스 메소드, 정규 표현식, 배열정렬 및 검색, 박싱,언박싱 (0) | 2019.04.15 |