2023. 5. 29. 22:40ㆍ자바
람다란?

람다식은 함수형 언어에서 온 개념으로 메소드를 간단한 식으로 표현할 때 사용합니다.
우리가 알고 있는 함수는 자바에서 메소드라는 이름으로 불리는데 왜냐하면 자바에서는 클래스 안에 선언을 해야 사용을 할 수 있기 때문이다.


1번 같은 경우에 타입이 있어야 되는 경우에는 ()를 사용해야 한다.
3번 같은 경우는 대부분 return문은 생략하기 때문에 있다는 거만 알고 넘어가자.


자바에서는 메소드만 사용될 수 없기 때문에 람다식은 익명 객체이다.
public class Ex14_0a {
public static void main(String[] args) {
Object obj = new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
};
int value = obj.max(3, 5);
// 오류 발생 왜냐하며 Object클래스 안에는 max라는 메소드가 존재하지 않기 때문
// 함수형 인터페이스를 활용하여 문제를 해결하자
}
}
함수형 인터페이스

public class Ex14_0a {
public static void main(String[] args) {
MyFunction2 obj = new MyFunction2() {
// 오버라이딩 - 접근제어자는 좁게 바꿀 수 없다. //상위클래스가 public이므로 public만 사용가능
public int max(int a, int b) {
return a > b ? a : b;
}
};
int value = obj.max(3, 5);
System.out.println("value = " + value);
// 람다식 사용
MyFunction2 f = (a, b) -> a > b ? a : b;
System.out.println("value = " + f.max(10, 4));
}
}
// 함수형인터페이스 어노테이션 사용 함수형 인터페이스 안에는 단 하나의 추상 메소드만 있어야 한다.
@FunctionalInterface
interface MyFunction2 {
// public abstract int max(int a, int b);
int max(int a, int b); // 인터페이스 안에서는 public abstract가 생략되어 있다.
}

@FunctionalInterface
interface MyFunction {
void run(); // public abstract void run();
}
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()") );
/*
출력값
f1.run()
f2.run()
f3.run()
f1.run()
run()
*/
}
}
java.util.function 패키지


Predicate인터페이스 사용시 Predicate<Integer, Boolean>으로 나오는게 맞지만 반환타입이 항상 Boolean이기 때문에 Boolean은 쓰지 않는다.

매개변수가 2개 이하인 경우 자바에서 만들어 놓은 함수형 인터페이스를 사용하자.
매개변수가 3개 이상은 사용자가 정의하여 사용하면 된다.

import java.util.function.*;
import java.util.*;
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());
}
}
}
predicate의 결합

import java.util.function.*;
class Ex14_3 {
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);
// f의 리턴값이 g의 입력값이 되어 g가 반환하는 값을 리턴
// 즉 f의 리턴값과 g의 입력값의 타입이 동일해야 정상 작동한다.
Function<Integer, Integer> h2 = f.compose(g); // g.andThen(f)와 같다.
System.out.println(h.apply("FF")); // "FF" → 255 → "11111111"
System.out.println(h2.apply(2)); // 2 → "10" → 16
Function<String, String> f2 = x -> x; // 항등 함수(identity function)
System.out.println(f2.apply("AAA")); // AAA가 그대로 출력됨
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));
System.out.println(all.test(150)); // true
String str1 = "abc";
String str2 = "abc";
// str1과 str2가 같은지 비교한 결과를 반환 equals() 메소드를 사용하고 있는거와 동일
Predicate<String> p2 = Predicate.isEqual(str1);
boolean result = p2.test(str2);
System.out.println(result);
}
}

import java.util.*;
class Ex14_4 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++)
list.add(i);
// list의 모든 요소를 출력
list.forEach(i -> System.out.print(i + ","));
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<>();
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 + "},"));
System.out.println();
/*
출력
* 0,1,2,3,4,5,6,7,8,9,
[1, 5, 7]
[10, 50, 70]
{1,1},{2,2},{3,3},{4,4},
* */
}
}
메소드 참조(method reference)

메서드 참조는 static 메소드 참조, 인스턴스 참조의 방식을 사용한다.
먼저 해당 예시에는 입력 값으로 String을 받고 Integer를 출력하고 있다.
입력과 출력이 있으므로 Function 인터페이스를 활용해준다.
람다식으로 나타내고 해당 람다식을 메서드 참조로 간단하게 표현할 수 있다.
클래스이름::메서드이름 형태로 나타낼 수 있다.

Supplier 인터페이스는 입력은 없고 출력만 있는 경우 사용하는 인터페이스다.
위의 예시처럼 메소드 참조로 변환하면서 많이 생략이 가능한 이유는 함수형 인터페이스에 선언되어 있기 때문이다.
예시를 살펴보도록 하자.
import java.util.function.Function;
import java.util.function.Supplier;
public class Ex14_0 {
public static void main(String[] args) {
// 람다식
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
System.out.println(f.apply("100") + 200); // 300
// 메소드 참조
Function<String, Integer> f2 = Integer::parseInt;
System.out.println(f.apply("200") + 500); // 700
// Supplier는 입력 x, 출력 ㅇ
Supplier<MyClass> s = () -> new MyClass();
System.out.println(s.get()); // javaofclassic.chapter14.MyClass@880ec60
MyClass mc = s.get();
System.out.println(mc); // javaofclassic.chapter14.MyClass@3f3afe78
// 메서드 참조로 변환 클래스 이름::메서드이름
Supplier<MyClass> s2 = MyClass::new;
System.out.println(s2.get()); //javaofclassic.chapter14.MyClass@36d64342
// Function은 입력ㅇ 출력ㅇ 람다식으로 표현
Function<Integer, MyClass2> ff = (i) -> new MyClass2(i);
MyClass2 mc2 = ff.apply(100);
System.out.println(mc2.iv); // 100
//Function 메소드 참조로 변환
Function<Integer, MyClass2> ff2 = MyClass2::new;
System.out.println(ff2.apply(500).iv); // 500
// 배열길이 입력 -> int형 배열 출력
Function<Integer, int[]> ff3 = (i) -> new int[i];
System.out.println(ff3.apply(333).length); //333
// 메소드 참조로 변환
Function<Integer, int[]> ff4 = int[]::new;
}
}
class MyClass{}
class MyClass2{
int iv;
public MyClass2(int iv) {
this.iv = iv;
}
}
'자바' 카테고리의 다른 글
| 자바 스트림 정리(자바의 정석) (0) | 2023.06.04 |
|---|---|
| 어노테이션 정리(자바의 정석) (0) | 2023.05.22 |
| enum 정리(자바의 정석) (0) | 2023.05.21 |
| 제네릭 정리 (자바의 정석) (0) | 2023.05.20 |
| 컬렉션 - hashMap 정리 (0) | 2023.05.17 |