728x90
람다식(Lambda Expression)
- 함수(메소드)를 간단한 '식(expression)'으로 표현하는 방법
- 익명 함수(이름이 없는 함수, anonymous function)
- 함수는 일반적인 용어, 메소드는 객체지향개념 용어. 함수는 클래스에 독립적, 메소드는 클래스에 종속적
- 람다식은 익명 함수가 아니라 익명 객체임
- 람다식(익명 객체)을 다루기 위한 참조변수가 필요. 참조변수 타입은? 함수형 인터페이스
람다식 작성하기
int max(int a, int b){
return a > b ? a : b;
}
- 1. 메소드의 이름과 반환타입을 제거하고 '->'를 블록{} 앞에 추가
(int a, int b) -> { return a > b ? a : b; } - 2. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ';' 안 붙임)
(int a, int b) -> a > b ? a : b - 3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)
(a, b) -> a > b? a : b
※주의사항
1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
2. 블록 안의 문장이 하나뿐 일 때, 괄호{} 생략가능(끝에 ';' 안 붙임) 단, 하나뿐인 문장이 return문이면 괄호{} 생략불가
package chapter14;
public class Study1_1 {
public static void main(String[] args) {
// Object obj = (a,b) -> a>b?a:b; //람다식. 익명객체
Object obj = new Object() {
int max(int a, int b) {
return a>b?a:b;
}
};
// int value = obj.max(3,5); //에러 발생. Object에 max()메소드를 가지고 있지 않음 → 함수형 인터페이스 사용
}
}
함수형 인터페이스
- 단 하나의 추상 메소드만 선언된 인터페이스
@FunctionalInterface
interface MyFunction{
public abstract int max(int a, int b);
}
MyFunction f = new MyFunction(){ //익명클래스. 클래스의 선언, 객체 생성을 동시에
public int max(int a, int b){
return a>b?a:b;
}
};
int value = f.max(3,5); //MyFunction에 max()가 존재하므로 가능
- 함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있음
단, 함수형 인터페이스의 메소드와 람다식의 매개변수 개수와 반환타입이 일치해야함.
MyFunction f = (a,b) -> a>b?a:b;
int value = f.max(3,5); //실제로 람다식(익명 함수)이 호출됨
package chapter14;
@FunctionalInterface //함수형 인터페이스는 단 하나의 추상 메소드만 가져야함
interface MyFunction2_1{
public abstract int max(int a, int b);
}
public class Study2_1 {
public static void main(String[] args) {
// MyFunction14_2 f = new MyFunction14_2() {
// @Override
// public int max(int a, int b) { //오버라이딩 규칙 - 접근제어자는 좁게 할 수 없음
// return a>b?a:b;
// }
// };
//람다식(익명객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 함
MyFunction2_1 f = (a,b) -> a>b? a:b; //람다식. 익명 객체
int value = f.max(3, 5);
System.out.println("value = " + value);
}
}
함수형 인터페이스 타입의 매개변수, 반환타입
package chapter14;
/**
* *함수형 인터페이스 타입의 매개변수, 반환타입
* -함수형 인터페이스 타입의 매개변수
* void exMethod(MyFunction f){
* f.myMethod(); //MyFunction에 정의된 메소드 호출
* }
* @FunctionalInterface
* interface MyFunction{
* void myMethod();
* }
* MyFunction f = () -> System.out.println("myMethod()"));
* exMethod(f);
* //두 줄을 한 문장으로
* exMethod(() -> System.out.println("myMethod()"));
* -함수형 인터페이스 타입의 반환타입
* MyFunction myFunction(){
* MyFunction f = ()->{};
* return f;
* }
* ==>
* MyFunction myMethod(){
* return () -> {};
* }
*/
@FunctionalInterface
interface MyFunction2_2{
void run(); // == public abstract void run();
}
public class Study2_2 {
static void execute(MyFunction2_2 f) { //매개변수 타입이 MyFunction2_2인 메소드
f.run();
}
static MyFunction2_2 getMyFunction2_2() { //반환타입이 MyFunction2_2인 메소드
// MyFunction2_2 f = () -> System.out.println("f3.run()");
// return f;
return ()-> System.out.println("f3.run()");
}
public static void main(String[] args) {
//람다식으로 MyFunction2_2의 run()을 구현
MyFunction2_2 f1 = () -> System.out.println("f1.run()");
MyFunction2_2 f2 = new MyFunction2_2() { //익명클래스로 run()을 구현
@Override
public void run() { //반드시 public을 붙여야함
System.out.println("f2.run()");
}
};
MyFunction2_2 f3 = getMyFunction2_2();
f1.run();
f2.run();
f3.run();
execute(f1);
execute(f2);
execute(f3);
execute(()->System.out.println("run()"));
}
}
java.util.function 패키지
package chapter14;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* *java.util.function 패키지
* -자주 사용되는 다양한 함수형 인터페이스를 제공(표준화에 도움이 됨)
* java.lang.Runnable : 매개변수도 없고, 반환값도 없음
* Supplier<T> : 매개변수는 없고 반환값만 있음. 공급자
* Consumer<T> : 매개변수만 있고 반환값이 없음. 소비자
* Function<T,R> : 일반적인 함수. 하나의 매개변수를 받아서 결과를 반환
* Predicate<T> : 조건식을 표현하는데 사용됨. 매개변수는 하나, 반환 타입은 boolean. 조건식
* -매개변수가 2개인 함수형 인터페이스
* BiConsumer<T,U> : 두 개의 매개변수만 있고, 반환값은 없음
* BiPredicate<T,U> : 조건식을 표현하는데 사용됨. 매개변수는 둘, 반환값은 boolean
* BiFunction<T,U,R> : 두 개의 매개변수를 받아서 하나의 결과를 반환
* -매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
* UnaryOperator<T> : Function의 자손, Function과 달리 매개변수와 결과의 타입이 같음.
* BinaryOperator<T> : BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같음.
*/
public class Study3_1 {
static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
List<T> newList = new ArrayList<T>(list.size());
for(T i : list) {
newList.add(f.apply(i));
}
return newList;
}
static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
System.out.print("[");
for(T i : list) {
if(p.test(i)) //짝수인지 검사
c.accept(i); //화면에 i 출력
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for(int i=0;i<10;i++) {
list.add(s.get());
}
}
public static void main(String[] args) {
Supplier<Integer> s = ()-> (int)(Math.random()*100)+1; //1~100난수
Consumer<Integer> c = i -> System.out.print(i+", ");
Predicate<Integer> p = i -> i%2==0; //짝수인지 검사
Function<Integer, Integer> f = i -> i/10*10; // i의 일의 자리를 없앤다.
List<Integer> list = new ArrayList<>();
makeRandomList(s, list); //list를 램덤값으로 채움
System.out.println(list);
printEvenNum(p, c, list); //짝수를 출력
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
}
}
Predicate의 결합
package chapter14;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* *Predicate의 결합
* -and(), or(), negate()로 두 Predicate를 하나로 결합(default 메소드)
* ※함수형 interface => default메소드, static메소드, 추상메소드
* //and() : &&, or() : ||, negate() : !
* Predicate<Integer> p = i -> i < 100;
* Predicate<Integer> q = i -> i <200;
* Predicate<Integer> r = i -> i%2==0;
* Predicate<Integer> notP = p.negate(); //i>=100
* Predicate<Integer> all = notP.and(q).or(r); // 100<=i && i<200 || i%2==0
* Predicate<Integer> all2 = notP.and(q.or(r)); // 100<=i && (i<200 || i%2==0)
* //test()를 이용
* System.out.println(all.test(2)); //true
* System.out.println(all2.test(2)); //false
* -등가비교를 위한 Predicate의 작성에는 isEqual()를 사용(static 메소드)
* String str1 = "abc";
* String str2 = "abc";
* Predicate<String> p = Predicate.isEqual(str1);
* Boolean result = p.test(str2);
* //두 줄을 한줄로
* boolean result = Predicate.isEqual(str1).test(str2);
*/
public class Study4_1 {
public static void main(String[] args) {
Function<String, Integer> f = (s) -> Integer.parseInt(s, 16);
Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
Function<String, String> h = f.andThen(g); // String -> Integer -> String. 함수 두개를 연결.
Function<Integer, Integer> h2 = f.compose(g);
System.out.println(h.apply("FF")); //"FF" → 255 → "11111111"
System.out.println(h2.apply(2)); //2 → "10" → 16
System.out.println("==========================");
Function<String, String> f2 = x -> x; //항등함수(identity function)
System.out.println(f2.apply("AAA")); //AAA가 그대로 출력
System.out.println("==========================");
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2==0;
Predicate<Integer> notP = p.negate(); //!p => i>=100
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150));
System.out.println("==========================");
String str1 = "abc";
String str2 = "abc";
//str1과 str2가 같은지 비교한 결과를 반환
Predicate<String> x = Predicate.isEqual(str1);
boolean result = x.test(str2);
System.out.println(result);
}
}
컬렉션 프레임워크와 함수형 인터페이스
package chapter14;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* *컬렉션 프레임워크와 함수형 인터페이스
* -함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메소드(와일드카드 생략)
* Collection 인터페이스
* => boolean removeif(Predicate<E> fillter) : 조건에 맞는 요소를 삭제
* List 인터페이스
* => void replaceAll(UnaryOperator<E> operator) : 모든 요소를 변환하여 대체
* Iterable 인터페이스
* => void forEach(Consumer<T> action) : 모든 요소에 작업 action을 수행
* Map 인터페이스
* => V compute(K key, BiFunction<K,V,V> f) : 지정된 키의 값에 작업 f를 수행
* V computeIfAbsent(K key, Function<K,V> f) : 키가 없으면, 작업 f 수행 후 추가
* V computeIfPresent(K key, BiFunction<K, V, V> f) : 지정된 키가 있을 때, 작업 f 수행
* V merge(K key, V value, BiFunction<V,V,V> f) : 모든 요소에 병합작업 f를 수행
* void forEach(BiConsumer<K,V> action) : 모든 요소에 작업 action을 수행
* void repaceAll(BiFunction<K,V,V>f) : 모든 요소에 치환작업 f를 수행
*/
public class Study4_2 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0;i<10; i++)
list.add(i);
//list의 모든 요소를 출력
list.forEach(i->System.out.print(i+","));
System.out.println();
//람다식이 아닐 경우
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next()+" ");
}
System.out.println();
//list에서 2 또는 3의 배수를 제거
list.removeIf(x->x%2==0||x%3==0);
System.out.println(list);
list.replaceAll(i->i*10); //list의 각 요소에 10을 곱함
System.out.println(list);
Map<String, String> map = new HashMap<String, String>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
//map의 모든 요소를 {k,v}의 형식으로 출력
map.forEach((k,v)->System.out.print("{"+k+", "+v+"}, "));
}
}
메소드 참조(method reference)
package chapter14;
import java.util.function.Function;
/**
* *메소드 참조(method reference)
* -하나의 메소드만 호출하는 람다식은 '메소드 참조'로 간단히 할 수 있음 => 클래스이름::메소드이름
* -static 메소드 참조
*/
public class Study5_1 {
public static void main(String[] args) {
// Function<String, Integer> f = (String s) -> Integer.parseInt(s);
Function<String, Integer> f = Integer::parseInt; //메소드 참조
System.out.println(f.apply("100")+200);
}
}
생성자의 메소드 참조
package chapter14;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* * 생성자의 메소드 참조
* -생성자와 메소드 참조
* Supplier<MyClass> s = () -> new MyClass();
* Supplier<MyClass> s = MyClass::new;
* Function<Integer, MyClass> s = (i) -> new MyClass(i);
* Function<Integer, MyClass> s = MyClass::new;
* -배열과 메소드 참조
* Function<Integer, int[]> f = x -> new int[x]; //람다식
* Function<Integer, int[]> f2 = int[]::new //메소드 참조. 배열타입[]::new
*/
class MyClass5_2{
int iv;
public MyClass5_2(int iv) {
this.iv = iv;
}
}
public class Study5_2 {
public static void main(String[] args) {
//Supplier 입력 X, 출력 O
// Supplier<MyClass5_2> s = () -> new MyClass5_2();
// Supplier<MyClass5_2> s = MyClass5_2::new;
// MyClass5_2 mc = s.get(); //MyClass5_2 객체 반환
// System.out.println(mc);
// System.out.println(s.get()); //또다른 객체
//생성자가 존재한다면, 입력과 출력이 생기므로 Function으로 바뀌어야함
// Function<Integer,MyClass5_2> f = (i) -> new MyClass5_2(i);
Function<Integer,MyClass5_2> f = MyClass5_2::new;
MyClass5_2 mc = f.apply(100);
System.out.println(mc.iv);
System.out.println(f.apply(100).iv);
Function<Integer, int[]> f2 = (i)-> new int[i];
Function<Integer, int[]> f3 = int[]::new;
System.out.println(f2.apply(200).length);
System.out.println(f3.apply(300).length);
}
}
728x90