16. 람다식
16.1 람다식 이란?
함수형 프로그래밍이란 함수를 정의 하고 이함수를 데이터 처리부로 보내 데이터를 처리하는 기법을 말한다.
반대는 <-> 메소드임. 함수는 객체와 상관없이 실행가능한 코드들을 묶어놓은것 메소드는 객체안에 존재하며 객체의 기능을 정리해둔것
따라서 자바에서는 맞지 않은데 다른데선 함수형이라고 부르기때문에 그럼.
데이터 처리부는 데이터만 가지고 있을 뿐 처리방법이 정해져있지않아 외부에서 제공된 함수에 의존한다.
데이터 처리방법을 함수형으로 받겟다.
자바8부터 람다식을 지원하고있다. 람다식은 데이터 처리부에 제공되는 함수 역할을 하는 매개변수를 가진 중괄호 블록이다.
데이터 처리부는 람다식을 받아 매개변수에 데이터를 대입하고 중괄호를 실행시켜 처리한다.
람다식: (매개변수, ...) -> {처리내용} 이름이없고 매개변수괄호와 화살표가 있다. 데이터를 어떻게 처리한다 밖에 없음.
그러나 자바는 객체 지향 임. 자바는 그래서 람다식도 익명 구현객체로 변환한다. 익명 구현 객체란 9장에서 설명한 것과 같이 이름이 없는 인터페이스 구현객체를 말한다.
예를들어 Calculable 인터페이스가 있다고 가정해보자. 추상메소드 딱 하나 있어야함.
함수를 처리할때 매개변수를 제공하는데 이 매개변수에 데이터가 들어간다.
데이터처리부의 데이터는 동일한데 외부에서 함수를 불러와서 처리하는 것이 함수형 프로그래밍이다.
함수를 다른것으로 전달하면 다양한 실행결과를 얻을 수 있다.
public interface Calculable{
//추상메소드
void calculate(int x, int y); //사용하려면 재정의필요
new Calculable() {
@Override
public void calculate(int x, int y){처리내용}
} 이것을 람다식으로 표현하면?
(x , y) -> {처리내용} 원래 구현방법
public void action(Calcuable calculable){
int x = 10;
int y = 4;
calculable.calculate(x, y);
//데이터를 제공하고 추상메소드를 호출
//처리 내용이없고 구현객체로 처리를해야함.action()메소드를 호출 할때 매개값으로 다음과 같이 람다식을 제공하룻 있다.
여기서 action()메소드는 제공되 람다식을 이용해서 내부데이터를 처리하는 처리부 역할을 한다.
위의 Calcuable calculable이분에 람다식을 넣는 것임.
action( (x,y) -> {
int result = x + y;
system.out.println(result);
});인터페이스의 익명구현객체를 람다식으로 표현하려면 인터페이스가 단 하나의 추상메소드만 가져야한다.
추상메소드가 두개이상인 인터페이스는 람다식으로 표현할 수 없다.
인터페이스가 단하나의 추상메소드를 가질때 이를 함수형 인터페이스(functional interface)라고 한다.
public interface Runnalbe{ void run();} 람다식 () -> {...}
인터페이스가 함수형 인터페이스임을 보장하기 위해서는 @FunctionalInterface 어노테이션을 붙이면된다.
선택사항이지만 컴파일 과정에서 추상메소드가 하나인지 검사하기 때문에 함수형 인터페이스를 작성할 수 있게 도와주는 역할을 한다.
package ch16.sec01;
@FunctionalInterface
public interface Calculabe {
//추상메소드
void calculate(int x, int y);}package ch16.sec01;
public class LamdaExample {
public static void main(String[] args) {
action((x,y) -> {
int result = x + y;
System.out.println("result: " + result);
});
action((x,y) -> {
int result = x - y;
System.out.println("result: " + result);
});
}
//메인함수 밖에다 작성
//호출 어디서함? 메인함수에서 햇음. 정적메소드이기땜에 그냥 가져다씀.
//Calculabe calculabe구현객체 자리에 람다식을 넣은거임
//여기는 데이터 선언부 역할을함.
public static void action(Calculabe calculabe) {
//데이터
int x =10;
int y = 4;
//데이터 처리
calculabe.calculate(x, y);}
} 
16.2 매개변수가 없는 람다식
함수형 인터페이스의 추상메소드에 매개변수가 없을 경우 람다식은 다음과 같이 작성할 수 있다.
Raunnalbe 의 run()과 같은 경우에 없다.
() -> {
실행문;
실행문;
} package ch16.sec02.exam01;
@FunctionalInterface
public interface Workable {
void work();} package ch16.sec02.exam01;
public class Person {
public void action(Workable workable) {
workable.work();
}}package ch16.sec02.exam01;
public class LamdaExample {
public static void main(String[] args) {
Person person = new Person();
//실행문 이 두개이산인 경우 중괄호 필요
person.action(() -> {
System.out.println("출근을 합니다.");
System.out.println("프로그래밍을 합니다.");
});
//실행문 한개일 경우 중괄호 생략가능
person.action(() ->
System.out.println("퇴근을 합니다."));
}}예제2는 9.6에서 실습한 익명구현객체의 예제를 수정한 것이다.
익명 구현객체를 람다식으로 대체해 버튼의 클릭이벤트를 처리하고잇다.
package ch16.sec02.exam02;
public class Button {
//정적 멤버 인터페이스
public static interface ClickListener{
void onClick();
}
//필드
private ClickListener clickListener;
//메소드
public void setClickLisntener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public void click() {
this.clickListener.onClick();
}} package ch16.sec02.exam02;
import ch09.sec06.exam03.Button;
public class ButtonExample {
public static void main(String[] args) {
//Ok버튼 객체 생성
Button btnOk = new Button();
//Ok버튼 클릭 이벤트를 처리할 ClickListner 구현 클래스(로컬 클래스)대신 람다식 주입
btnOk.setClickLisntener(()->{
System.out.println("Ok버튼을 클릭했습니다.");
});
//Ok버튼 클릭하기
btnOk.click();
/*
//Ok버튼 클릭 이벤트를 처리할 ClickListner 구현 클래스(로컬 클래스) 원래이렇게함.
class OkListener implements Button.ClickListener //Button클래스 안의 인터페이스 참조
{
@Override
public void onClick() {
System.out.println("Ok 버튼을 클릭했습니다.");
}
}
//Ok 버튼 객체에 ClickLisnter구현객체 주입
btnOk.setClickLisntener(new OkListener());
*/
//--------------
//Cancel 버튼객체 생성
Button btnCancel = new Button();
//Cancel 버튼 클릭 이벤트를 처리할 ClickListener 구현 클래스(로컬클래스) 대신 람다식 제공
btnCancel.setClickLisntener(()->{
System.out.println("Cancle버튼을 클릭햇습니다.");
});
//Cancel 버튼 클릭하기
btnCancel.click();
}}16.3 매개변수가 있는 람다식
함수형 인터페이스의 추상메소드에 매개변수가 있을 경우 다음과 같이 선언할 수 있다.
매개변수를 선언할때 타입은 생략할 수 있고 구체적이 ㄴ타입 대신에 var을 사용할 수도 있다. 하지만 타입을 생략하고작성하는 것이 일반적이다.
매개변수가 하나일 경우 괄호()를 생략할 수 있다.
매개변수 -> {
실행문;
실행문;
}package ch16.sec03;
@FunctionalInterface
public interface Workable {
void work(String name, String job);
}package ch16.sec03;
@FunctionalInterface
public interface Speakable {
void speak(String content);
}package ch16.sec03;
public class Person {
public void action1(Workable workable) {
workable.work("홍길동", "프로게이머");
}
public void action2(Speakable speakable) {
speakable.speak("안녕하세요");
}}package ch16.sec03;
public class LamdaExample {
public static void main(String[] args) {
Person person = new Person();
//매개변수가 두개일 경우
//이 매개변수가 꼭 같은 이름일 필요는 없고 n, j으로 해도됨.
person.action1((name, job) -> {
System.out.println(name + "이 ");
System.out.println(job + "을 합니다.");
});
//실행문이 하나일경우
person.action1((name, job) ->
System.out.println(name + "이 " + job + "을 하지 않습니다."));
//매개변수가 하나일 경우 ()생략가능
person.action2(word -> {
System.out.println("\"" + word + "\"");
System.out.println("라고 말합니다.");
});
//실행문이 하나일경우
person.action2((word) ->
System.out.println("\"" + word + "\"" + "라고 외칩니다."));
}}
16.4 리턴값이 있는 람다식
추상메소드에 리턴타입이 있을 경우 리턴을 사용해야함.
package ch16.sec04;
@FunctionalInterface
public interface Calculabe {
//추상메소드
double calc(double x, double y);
}package ch16.sec04;
public class Person {
public void action(Calculabe calculabe) {
double result = calculabe.calc(10, 4);
System.out.println("결과: " + result);
}} package ch16.sec04;
public class LamdaExample {
public static void main(String[] args) {
Person person = new Person();
//실행문이 두개이상일 경우
person.action((x, y)->{
double result = x + y;
return result;
});
//리턴문이 하나만 있을경우(연산식)
//죄다 생략가능 하나의값만 올경우 = 리턴값임.
/*
person.action((x, y)->{
return = x + y;
});
*/
person.action((x, y)-> (x + y));
//리턴문이 하나만 있을경우(메소드 호출)
//메소드가 하나의 리턴값을 가져올 경우
//sum메소드가 리턴 값 그자체이기때문에 위의 방식을 이용해서 해결가능.
/*
person.action((x, y)->{
return sum(x,y);
});
*/
person.action((x, y)-> sum(x,y));
}
public static double sum(double x , double y) {
return (x + y);
}}16.5 메소드 참조
메소드 참조는 말그대로 메소드를 참조해서 매개변수의 정보 및 리턴 타입을 알아내 람다식에서 불필요한 매개변수를 제거하는 것을 목적으로 한다.
예를들어 두개의 값을 받아 큰 수를 리턴하는 Math클래스의 max()정적메소드를 호출하는 람다식은 다음과 같다.
(left, right) -> Math.max(left, right);
람다식은 단순히 두개의 값을 Math.max()메소드의 매개값으로 전달하는 역할만하기 때문에 다소 불편해 보인다.
이 경우에는 다음과 같이 메소드 참조를 이요하면 매우 깔끔하게 처리가능하다.
Math :: max;
16.5.1 정적메소드와 인스턴스 메소드 참조
정적메소드를 참조할 경우 클래스이름 :: 정적메소드이름
인스턴스 메소드일 경우에는 먼저 객체를 생성한 다음 참조변수 :: 인스턴스메소드이름
메소드참조의 경우 가독성이 더떨어진다 싶다면 그냥 하던대로 해도된다. 누가 뭐라 안함!
package ch16.sec05.exam01;
@FunctionalInterface
public interface Calculabe {
//추상메소드
double calc(double x, double y);
}package ch16.sec05.exam01;
public class Person {
public void action(Calculabe calculabe) {
double result = calculabe.calc(10, 4);
System.out.println("결과: " + result);
}} package ch16.sec05.exam01;
public class Computer {
public static double staticMethod(double x, double y) {
return x + y;
}
public double instanceMethod(double x, double y) {
return x * y;
}} package ch16.sec05.exam01;
public class MethodReferenceExample {
public static void main(String[] args) {
Person person = new Person();
//정적메소드일경우
//람다식
person.action((x, y) -> Computer.staticMethod(x,y));
//메소드 참조
person.action(Computer :: staticMethod);
//인스턴스 메소드일경우
Computer com = new Computer();
//람다식
person.action((x,y) -> com.instanceMethod(x, y));
//메소드참조
person.action(com :: instanceMethod);
}} 
16.5.2 매개변수의 메소드 참조
(a,b) -> {a.instanceMethod(b);} a가 가지고 있는 메소드를 호출할때 b를 매개값으로 제공한다.
이것을 메소드 참조로 표현하면 정적메소드참조랑 비슷하다.
클래스 :: instanceMethod
구분을 어케하냐? 클래스 :: 인스턴스메소드이름 이면 {a.instanceMethod(b);}으로인식한다.
package ch16.sec05.exam02;
@FunctionalInterface
public interface Comparable {
int compare(String a, String b);
} package ch16.sec05.exam02;
public class Person {
public void ordering(Comparable comparable) {
String a = "홍길동";
String b = "김길동";
int result = comparable.compare(a,b);
if (result <0) {
System.out.println(a+ "은 " + b + "보다 앞에 옵니다.");
} else if (result == 0) {
System.out.println(a+ "은 " + b + "과 같습니다.");
} else {
System.out.println(a+ "은 " + b + "보다 뒤에 옵니다.");
}}} package ch16.sec05.exam02;
public class MethodReferenceExample {
public static void main(String[] args) {
Person person = new Person();
person.ordering((a,b)-> a.compareToIgnoreCase(b));
person.ordering(String :: compareToIgnoreCase);
}} 
16.6 생성자 참조
생성자를 참조한다는 것은 객체를 생성하는 것을 의미한다.
람다식이 단순히 객체를 생성하고 리턴하도록 구성된다면 람다식을 생성자 참조로 대치할 수 있다.
다음 코드를 보면 람다식은 단순히 객체를 생성한 후 리턴만한다.
(a,b) -> {return new 클래스(a,b);}
이것을 생성자 참조로 표현하면 다음과같다.
클래스이름 :: new
생성자가 오버로딩되어 여러개가 있을 경우 컴파일러는 함수형 인터페이스의 추상메소드와 동일한 매개변수타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
만약 해당 생성자가 존재하지 않으면 컴파일 오류가 발생한다.
package ch16.sec06;
@FunctionalInterface
public interface Creatable1 {
public Member create(String id);
}package ch16.sec06;
@FunctionalInterface
public interface Creatable2 {
public Member create(String id, String name);
}package ch16.sec06;
public class Member {
private String id;
private String name;
public Member(String id) {
this.id = id;
System.out.println("Member(String id)");
}
public Member(String id, String name) {
this.id = id;
this.name = name;
System.out.println("Member(String id. String name)");
}
@Override
public String toString() {
String info = "{ id: " + id + ", name: " + name + "}";
return info;
}
}package ch16.sec06;
public class Person {
public Member getMember1(Creatable1 creatable) {
String id = "winter";
//id를 매개값으로주고 멤버객체를 만들어서 리턴해라
Member member = creatable.create(id);
return member;
}
public Member getMember2(Creatable2 creatable) {
String id = "winter";
String name = "한겨울";
Member member = creatable.create(id, name);
return member;
}} package ch16.sec06;
public class ConstrutctorReferenceExample {
public static void main(String[] args) {
Person person = new Person();
//생성자 호출 원래 람다식 사용한것
Member m3 = person.getMember1((id) -> {
Member m = new Member(id);
return m;
});
//생성자 호출 생략
Member m1 = person.getMember1(Member :: new);
System.out.println(m1);
System.out.println();
Member m2 = person.getMember2(Member :: new);
System.out.println(m2);
System.out.println();
}} 
2022.12.02 리뷰
자바의 끝이 보이기 시작했다.
100% 이해는 하지 못하지만 천천히 나아가고 있다.
중요한것은 꺾이지 않는 마음.
'기초단계 > JAVA' 카테고리의 다른 글
| 2022.12.05 JAVA 스트림요소처리 (1) | 2022.12.05 |
|---|---|
| 2022.12.03 JAVA 스트림요소처리 (1) | 2022.12.03 |
| 2022.12.01 JAVA 컬렉션 자료구조 (0) | 2022.12.02 |
| 2022.11.30-2 JAVA 컬렉션 자료구조 (1) | 2022.11.30 |
| 2022.11.30-1 JAVA 멀티 스레드 (1) | 2022.11.30 |