[Java] 자바 #46, 람다식의 함수형 인터페이스 개념 및 예제
- 프로그래밍 언어/Java
- 2019. 3. 17. 13:38
표준 API함수적 인터페이스
- JDK 1.8 부터 제공
- java.util.function 패키지
- 함수적 인터페이스 집합 패키지
- 오로지 람다식만을 지원하기 위해 만들어진 인터페이스 모음
- 람다식 타겟 타입 = 표준 API 함수적 인터페이스 + 사용자 정의 인터페이스
표준 API함수적 인터페이스 종류
- 추상메소드 딱 1개
1. Consumer : 매개변수O, 반환값X -> 추상메소드 제공.
2. Supplier : 매개변수X, 반환값O
3. Function : 매개변수O, 반환값O, 주로 매개변수를 반환값 타입으로 변환 후 반환
4. Operator : 매개변수O, 반환값O, 주로 매개변수를 연산 후 결과값 반환 역할
5. Predicate : 매개변수O, 반환값O, 주로 매개변수를 조사한 후 논리값 반환 역할
함수적 인터페이스 예제1, Consumer
1. Consumer 매개변수O, 반환값X
- accept() 메소드를 제공하는 함수형 인터페이스
- 매개변수를 받아서 소비하는 일을 구현하는 역할.
- 다양한 오버로딩 지원.
다음과 같은 인터페이스가 있을 떄!
1 2 3 4 | @FunctionalInterface interface MyConsumer { void accept(String txt); } | cs |
- @FunctionalInterface 는 해당 인터페이스를 함수적 인터페이스로 사용하겠다는 어노테이션
- 만약 추상메소드를 하나 더 선언하게되면 문법오류
- Invalid '@FunctionalInterface' annotation; MyConsumer is not a functional interface
기존 람다식 이용
ex)
MyConsumer c1 = (txt) -> {
System.out.println("결과 : " + txt);
};
c1.accept("홍길동");
Consumer 이용
ex)
Consumer<String> c2 = (txt) -> System.out.println("결과 : " + txt);
c2.accept("홍길동");
- Consumer 인터페이스 -> 추상메소드, 인자값 1개 -> 소비 메소드 구현부. ? 전달된 매개변수를 어떤식으로 사용하는지는 제한 없음.(구현하는 개발자 마음)
- 매개변수 1개짜리 Consumer
Consumer<Integer> c3 = count -> {
for (int i = 0; i < count; i++) {
System.out.println(i);
}
};
c3.accept(10);
매개변수 2개짜리 Consumer
ex)
BiConsumer<String, Integer> bc = (name, age) -> {
System.out.println("이름 : " + name);
System.out.println("나이 : " + age);
System.out.println("결과 : " + ((age > 19) ? "어른" : "아이"));
};
bc.accept("홍길동", 25);
Consumer 심화 예제
1 2 3 | interface MyNumber { int getNum(); } | cs |
다음과 같은 MyNumber 인터페이스가 있을 때!
Consumer<MyNumber> c4 = num -> System.out.println(num.getNum());
c4.accept(new MyNumber() { A
@Override
public int getNum() {
return 100;
}
});
c4.accept(() -> { B
return 200;
});
예제 해설 >>
Consumer는 MyNumber 객체를 매개변수로 갖는 accept(MyNumber num) 형태로 선언하고 그 accept 메소드 내부구현은 에로우 다음에나오는 System.out.println(num.getNum()); 부분입니다.
즉, accept() 안에다가 MyNumber 를 구현한 객체든 익명객체든 넣어줘야 한다.
A부분은 기존 익명객체를 만드는 방법으로 getNum 메소드를 구현한 익명객체를 넣어주었고, B부분은 람다식을 이용해 익명객체를 만들어 대입하였다. 그 객체들의 getNum 메소드는 각각 100과 200을 리턴하게 되므로 순차적으로 100을 출력하고 200을 출력하는 결과를 보이게 된다.
함수적 인터페이스 예제2, Supplier
2. Supplier 매개변수X , 반환값O
- getXXX() 매ㅔ소드를 제공하는 함수형 인터페이스
- 데이터를 공급하는 역할.
기본 예제
ex)
Supplier<Integer> s1 = () -> {
return 100;
};
System.out.println(s1.get());
다음과 같은 클래스가 있을때는!
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 31 32 33 34 35 | class User { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return String.format("%s(%s)", this.name, this.age); } } | cs |
Supplier<User> s3 = () -> new User("아무개", 30);
User u = s3.get();
System.out.println(u);
>> 이런식으로 객체를 생성하는 생성자를 담을 수도 있다.
Supplier<List<User>> s4 = () -> {
List<User> list = new ArrayList<User>();
list.add(new User("아무개", 20));
list.add(new User("우성환", 25));
list.add(new User("김김김", 23));
return list;
};
for (User user : s4.get()) {
System.out.println(user);
}
혹은 이런식으로 컬렉션을 반환타입으로 삼고 작업을 한후 해당 컬렉션을 반환하는 형태로도 쓸 수 있다.
>> 즉, 어떤 타입을 반환타입으로 선언하여 return 하는 형태이므로 반환하는 형태를 정해놓고 짜임새 있게 사용하는 방법이 Supplier를 사용하는데 가장 중요한 의미라고 할 수 있다.
함수적 인터페이스 예제3, Function
함수형 인터페이스
- 람다식을 저장하는 용도(내부적으로는 람다식으로 만들어진 메소드를 소유한 익명객체를 생성 + 저장)
1. 값 -> Consumer -> (소비)
-void accept(값)
2. () -> Supplier -> 값 반환.
-값 get()
3. 값 -> Function -> 값 반환
- 값 apply(값)
Function
- 매개변수와 반환값이 있는 메소드를 제공한다.
- applyXXX()메소드를 제공하는 함수형 인터페이스.
- 매개값을 반환값으로 매핑(변환)하는 역할.
- Function<매개변수타입, 반환값 타입>
Function<String, Integer> f1 = (txt) -> { txt 매개변수는 String이고 나는 Integer를 반환할거야
return txt.length();
};
System.out.println(f1.apply("동해물과 백두산이 마르고 닳도록")); //17
Function<Integer, String> f2 = num -> {
return num > 0 ? "양수" : "음수";
};
System.out.println(f2.apply(7)); "양수"
System.out.println(f2.apply(-1)); "음수"
다음과 같이 Mouse 클래스가 있을 때! (Comparable 구현)
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class Mouse implements Comparable { private String name; private int price; public Mouse() { } public Mouse(String name, int price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } @Override public String toString() { // TODO Auto-generated method stub return String.format("[이름 : %s]\n[가격 : %d]\n", this.name, this.price); } @Override public int compareTo(Object m) { if (m instanceof Mouse) { Mouse m2 = (Mouse) m; if (this.name.equals(m2.getName()) && this.price == m2.price) { return 1; } } else { return -1; } return 0; } } | cs |
Function<Mouse, Integer> f3 = mouse -> (int) (mouse.getPrice() * 1.1);
>> 매개변수 mouse는 Mouse 클래스형이고, 객체로 생성한 생성자의 매개값을 받아서 Price를 초기화한 값으로 1.1을 곱해서 반환하겠다.
System.out.println(f3.apply(new Mouse("TTT-5455", 1250)));
>> 1375
함수적 인터페이스 예제4, Operator
Operator
- 매개변수와 반환값이 있는 메소드를 제공한다.
- applyXXX()
- Function의 하위 호환 인터페이스(서브셋)
- 주로 매개값을 연산(***)을 통해 결과값을 만들어 내고 그 값을 반환.
- Function과 큰 차이는 Operator는 매개값과 반환값의 타입이 동일하다.(연산자들의 특징)
ex 1)
BinaryOperator<Integer> b1 = (n1,n2) -> n1+n2;
System.out.println(b1.apply(3, 5));
ex 2)
BinaryOperator<String> b2 = (firstName, lastName) -> lastName+"가 "+firstName;
System.out.println(b2.apply("길동", "홍"));
함수적 인터페이스 예제5, Predicate
Predicate
- 매개변수와 반환값이 있는 메소드를 제공한다.
- testXXX()
- 매개값을 사용해 조사(조건) 후 논리값을 반환한다.
- Function 서브 셋.
Predicate<Integer> p1 = n -> n>0;
System.out.println(p1.test(0)?"양수":"양수가 아니다.");
Predicate<String> p2 = s -> s.length()>10;
System.out.println(p2.test("홍길동입니다.")?"긴 문장":"짧은 문장");
Predicate<Student> p3 = s -> (s.getEng()+s.getKor()+s.getMath())>=240;
System.out.println(p3.test(new Student("홍길동",100,80,90))?"합격":"불합격");
- 매개변수가 2개
BiPredicate<String, String> p4 = (s1,s2) -> s1.length()>s2.length();
System.out.println(p4.test("홍길동", "홍민"));
함수적 인터페이스 심화, 인터페이스 합치기
인터페이스를 합치는 작업.
- 여러 함수형 인터페이스를 하나의 인터페이스로 합치는 작업
1. andThen()
- A.andThen(B) -> A와 B 인터페이스의 조합
- A실행 -> B실행.
2. compose()
- A.compose(B)
- B실행 -> A실행
간단예제
Consumer<Mouse> c1 = m -> System.out.println(m.getName());
Consumer<Mouse> c2 = m -> System.out.println(m.getPrice());
Mouse m1 = new Mouse("TT-543 광마우스", 50000);
요구사항 -> m1의 이름과 가격을 출력하라
c1.accept(m1);
c2.accept(m1);
// c3호출 -> c1호출 + c2호출;
Consumer<Mouse> c3 = c1.andThen(c2); c1 실행하고 c2실행하겠다.
c3.accept(m1);
Consumer<Mouse> c4 = c2.andThen(c1);
c4.accept(m1);
Function 예제
- Student 클래스가 다음과 같이 있을 때!
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | class Student { private String name; private int kor; private int eng; private int math; public Student() { } public Student(String name, int kor, int eng, int math) { this.name = name; this.kor = kor; this.eng = eng; this.math = math; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getKor() { return kor; } public void setKor(int kor) { this.kor = kor; } public int getEng() { return eng; } public void setEng(int eng) { this.eng = eng; } public int getMath() { return math; } public void setMath(int math) { this.math = math; } @Override public String toString() { // TODO Auto-generated method stub int score = this.getEng() + this.getKor() + this.getMath(); return String.format("이름 : %s,총점 : %d\n", this.name, score); } } | cs |
Student student = new Student("아무개",100,95,90);
Function<Student, Integer> f1 = s -> s.getKor() + s.getEng() + s.getMath(); // 총점반환.
Function<Integer, String> f2 = n -> n >= 240 ? "합격" : "불합격"; // 점수로 -> 합격여부 반환
int total = f1.apply(student); // 총점반환.
String result = f2.apply(total); // 합격여부 반환
System.out.println(result);
Function<Student, String> f3 = f1.andThen(f2);
System.out.println(f3.apply(student)); 이 두개는 결과가
Function<Student, String> f4 = f2.compose(f1);
System.out.println(f4.apply(student)); 같다.
Predicate에만 있는 기능
- 자바의 논리연산자와 같은 역할.
1. and()
2. or()
3. negate()
4. isEquals()
Predicate<Integer> p1 = n -> n % 2 == 0;
Predicate<Integer> p2 = n -> n % 5 == 0;
int num = 4;
if (p1.test(num)) {
System.out.println("2의 배수입니다.");
} else if (p2.test(num)) {
System.out.println("5의 배수입니다.");
}
Predicate<Integer> p3 = p1.and(p2);
if (p3.test(num)) {
System.out.println("2와 5의 공배수입니다.");
}
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] 자바, 가비지 컬렉터(Garbage Collector) (0) | 2019.04.21 |
---|---|
[Java] 자바 #47, 스트림의 개념 및 예제 (0) | 2019.03.17 |
[Java] 자바 #45, 람다식 예제 및 활용 (3) | 2019.03.16 |
[Java] 자바 #44, String split(), StringTokenizer 클래스 ( 문자열 분리 , 쪼개기) (5) | 2019.03.11 |
[Java] 자바 #43, 정규표현식 설명 및 예제 (5) | 2019.03.10 |