
람다식은 메서드를 하나의 식으로 표현한 것. 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.
메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로 람다식을 익명함수라고도 한다. -> 메서드를 변수처럼 다루게 해줌
int[] arr = new int[5];
Arrays.setAll(arr, (i) ->(int)(Math.random()*5)+1);
//위의 람다식은 아래의 함수와 같은 역할을 수행한다.
int method(i){
return (int) (Math.random()*5)+1;
}
람다식은 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 ->를 추가한다
반환 값이 있는 경우 return문 대신 식의 연산결과가 자동적으로 반환값이 되도록 할 수 있다. 이때는 끝에 ;를 붙이지 않는다
람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략할 수 있다. 반환타입이 없는 이유도 추론이 가능하기 때문.
(단, 매개변수중 어느 하나의 타입만 생략하는 것은 허용되지 않는다.)
선언된 매개변수가 하나뿐인 경우에는 괄호를 생략할 수 있다. 단, 매개변수의 타입이 있으면 괄호를 생략할 수 없다.
int max(int a,int b) { return a>b? a:b}
-> (a,b) -> a>b? a:b
void printVar(String name, int i) { System.out.println(name+"-"+i);}
-> (name, i) -> System.out.println(name + "=" + i)
int roll(){ return (int)(Math.random() * 6) ; }
-> () -> (int)(Math.randomm() * 6)
람다식은 익명 클래스의 객체와 동등
(int a, int b) -> a>b ? a:b
// 밑과 동일
new Object(){
int max(int a, int b){
return a>b? a:b;
}
}
// 호출시에는 참조변수를 통해
타입 f = (int a, int b)-> a>b ? a:b;
// 참조변수인 f의 타입은 클래스 또는 인터페이스가 가능
람다식을 다루기 위한 인터페이스를 함수형 인터페이스라 부르며 이를 통해 람다식을 다룬다
// max가 선언된 MyFunction 인터페이스가 있다고 가정
interface Myfunction{
public abstract int max(int a, int b);
}
// 이 인터페이스를 구현한 익명 클래스의 객체는 다음과 같다
MyFunction f = new MyFunction(){
public int max(int a, in b){
return a>b? a:b;
}
};
int big = f.max(5,3)
/* 여기서 MyFunction인터페이스에 정의된 메서드 max()는
람다식 (int a, int b) ->a > b? a:b와 메서드의 선언부가 일치 하기에 람다식으로
대체가능 */
MyFunction f = (int a, int b) -> a > b? a:b;
int big = f.max(5,3);
// max와 람다식의 매개변수 타입과 개수, 반환값이 일치하기에 가능
@FunctionalInterface
interface MyFunction{
// 람다식과 인터페이스가 1:1로 연결되기 위해 하나의 추상메서드만 정의되어 있어야함
public abstract int max(int a, int b);
}
메서드의 매개변수가 함수형 인터페이스라면 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야한다.
(참조변수 없이 직접 람다식을 매개변수로 지정하는 것도 가능하다.)
@FunctionalInterface
interface Myfunction{
void myMethod(); // 추상 메서드
}
void aMethod(MyFunction f){// 매개변수 타입이 함수형 인터페이스
f.myMethod(); // Myfunction에 정의된 메서드 호출
}
...
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f)
aMethod(() -> System.out.println("Mymethod()")));
메서드의 반환타입이 함수형 인터페이스타입이라면 함수형 인터페이스의 추상메서드와 동등한 람다식을 가리키는 참조변수를 반환하거나 람다식을 직접 반환할 수 있다.
MyFunction myMethod(){
MyFunction f = () -> {};
return f; // 한줄로 하면 return () -> {};
}
람다식은 익명 객체이고 익명 객체는 타입이 없다. 따라서 대입 연산자의 양변의 타입을 일치시키기 위해 형변환이 필요하다.
람다식은 인터페이스를 구현한 클래스 객체와 완전히 동일하기 때문에 동일한 인터페이스로의 형변환을 허용한다. 이러한 형변환은 생략가능하다.
람다식은 객체이지만 Object타입으로 형변환 할 수 없다. 람다식은 오직 함수형 인터페이스로만 형변환이 가능하다.
람다식 내에서 참조하는 지역변수는 final이 붙지 않았어도 상수로 간주된다. 반면 클래스 인스턴스 변수는 상수로 간주되지 않아 값을 변경할 수 있다. (외부 지역변수와 같은 이름의 람다식 매개변수는 허용되지 않는다.)
package ch14;
@FunctionalInterface
interface MyFunction{
void run(); // public abstract void run();
}
public class Ex14_1 {
static void execute(MyFunction f) { // 매개변수의 타입이 MyFunction인 메서드
f.run();
}
static MyFunction getMyFunction() { // 반환 타입이 MyFunction인 메서드
MyFunction f = () -> System.out.println("f3.run()");
return f;
}
public static void main(String[] args) {
// 람다식으로 MyFunction의 run() 구현
MyFunction f1 = ( )-> System.out.println("f1.run()");
MyFunction f2 = new MyFunction() { // 익명클래스로 run 구현
public void run() { // public을 반드시 붙여야함
System.out.println("f2.run()");
}
};
MyFunction f3 = getMyFunction();
f1.run();
f2.run();
f3.run();
execute(f1);
execute( ()-> System.out.println("run()") );
}
}
java.util.function 패키지에는 일반적으로 자주 쓰이는 형식의 메서드가 미리 정의되어 있다.
재사용성이나 유지보수 측면에서 가능하면 매번 새로운 함수형 인터페이스를 정의하지 말고, 이 패키지의 인터페이스를 활용하는 것이 좋다.

Predicate는 Function의 변형으로 반환타입이 boolean이라는 것만 다르다
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";
if(isEmptyStr.test(s))
System.out.println("This is an empty String.");
UnaryOperator<T>와 BinaryOperator<T>는 Function의 자손 인터페이스로 매개변수의 타입과 반환 타입이 모두 일치한다.
(타입이 같아서 하나만 적음)
package ch14;
import java.util.function.*;
import java.util.*;
public class Ex14_2 {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int)(Math.random()*100) + 1;
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);
System.out.println(list);
printEvenNum(p,c,list);
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
}
//람다식을 매개변수로 하는 것
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);
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T>s, List<T>list ) {
for(int i = 0; i < 10; i++ ) {
list.add(s.get());
}
}
}
[52, 45, 78, 44, 76, 42, 85, 63, 83, 53]
[52,78,44,76,42,]
[50, 40, 70, 40, 70, 40, 80, 60, 80, 50]
and(), or(), negate()를 연결하여 하나의 새로운 Predicate로 결합 가능
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;
// 여러 조건식을 하나로 합친 경우( 참조 변수 대신 조건식을 바로 넣어야됨)
// 100 <= i && (i < 200 || i %2 ==0 )
Predicate<Integer> all = notP.and(q.or(r));
// isEqual() -> 두 대상을 비교하는 Predicate를 만들 때 사용
//
Predicate<String> p = Predicate.isEqual(str1)
boolean result = p.test(str2)
Predicate 끝에 negate()를 붙이면 조건식 전체가 부정
package ch14;
import java.util.function.*;
public class Ex14_3 {
public static void main(String[] args) {
Predicate<Integer>p = i -> i < 100;
Predicate<Integer>q = i -> i <200;
Predicate<Integer>r = i -> i%2 ==0;
Predicate<Integer> notP = p.negate();
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150));
String str1 = "abc";
String str2 = "abc";
// isEqual로 매개변수로 비교대상 하나, test()로 나머지 한개 매개변수로 지정.
Predicate<String>p2 = Predicate.isEqual(str1);
boolean result = p2.test(str2);
System.out.println(result);
}
}

list.forEach(i->System.out.print(i + ", "); // list의 모든 요소 추력
list.removeIf(x -> x % 2 == 0 || x % 3 == 0); // 2 또는 3의 배수를 제거
list.replaceAll(i -> i * 10); // 모든 요소에 10을 곱함
// map의 모든 요소를 {k, v}의 형식으로 출력
map.forEach((k, v) -> System.out.print("{"+k+", "+v+"}, "));
forEach로 예전에 Iterator로 while문 돌리던 컬렉션 프레임워크 코드를 아주 간단히 대체할 수 있음
하나의 메서드만 호출하는 람다식은 클래스이름::메서드이름 또는 참조변수 :: 메서드이름 으로 바꾸기 가능

// 문자열을 정수로 변환하는 람다 식
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
// 바꾼 후
Function<String, Integer> f = Integer::parseInt;
Supplier<MyClass> s = () -> new MyClass(); //생성자
Supplier<MyClass> s = MyClass::new; // 메서드 참조
// 매개변수가 있는 생성자
BiFunction<Integer, String, MyClass> bf = (i, s) -> new MyClass(i,s);
BiFunction<Integer, String, MyClass> bf2 = MyClass:: new; // 메서드 참조
// 배열 생성
Function<Integer, int[]> f = x-> new int[x];
Function<Integer, int[]> f2 = int[] :: new;