Java

[Java] Lamda 람다식

소댓 2023. 4. 9. 17:15

* 람다식

 

- 메서드(함수)를 하나의 식으로 표현한 것

- 문법 : (매개변수 목록 -> {실행문}

 

- 장점
 1. 코드를 간결하게 만들 수 있다.
 2. 코드가 간결하고 식에 개발자의 의도가 명확하게 드러나므로 가독성이 향상된다.
 3. 함수(메서드)를 만드는 과정 없이 한 번에 처리할 수 있기에 코딩하는 시간이 줄어든다.

- 단점
 1. 익명 함수는 재사용이 불가능하다. 
 2. 디버깅이 다소 까다롭다.
 3. 람다식을 남발하면 코드가 지저분해진다.(비슷한 코드의 중복 가능성 높음)
 4. 재귀로 만들 경우에는 다소 부족한 면이 있다. (재귀함수 : 자신을 재참조하는 함수)

 

  

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
59
package day25;
 
// 람다식 ==> 메서드(함수)를 하나의 식으로 표현한 것
 
//하나의 자바 파일에 하나의 클래스만 둘 수 있음
//밀접한 경우에는 클래스나 interface가 얹혀져 있을 수 있음
interface Oper{
    public int addOne(int a);
}
 
 
// 인터페이스 구현 > 클래스! 따라서, 추상메서드 override 필요
class Test implements Oper{
    @Override
    public int addOne(int a) {
//        return 0;
        return a*2// add3에서 a*2추가
    }
    
}
public class LamdaEx1 {
 
    public static void main(String[] args) {
        
        // 인터페이스는 new 불가
        //          ==> 인스턴스화 할 수 없음
//        Oper add = new Oper(); >> 불가능!
        
        // Oper add = 인터페이스를 구현한 객체;
//        Test t = new Test(); // 위에서 클래스화(인터페이스 구현)해서 인스턴스화 가능
//        Oper add = t; // 따라서, 참조값 주는 것이 가능
        
        // 하지만 한 번만 쓸거라면 굳이 클래스화할 필요 없음 > 이름 주지 않고 그냥 만듦
        // Test 클래스 만들지 말고, 
        Oper add = new Oper() { // add unimplements method : 미구현 메서드 추가 >> 익명 이너 클래스
            // 익명 이너 클래스 : 내부에서 정의하는, 이름이 없는 클래스..
            @Override
            public int addOne(int a) {
                // TODO Auto-generated method stub
                return a*2;
            }
            
        }; // add end
        
        System.out.println(add.addOne(100)); // addOne의 인수를 주고 리턴
        
        
        // 줄여서 쓰는 방법..<람다식> 두가지 다 사용 가능
        Oper add2 = (a) -> a*2;
        System.out.println(add2.addOne(100));
        
        Oper add3 = new Test();
        System.out.println(add3.addOne(100));
        
    }
    
    
}
 
cs

 

 

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
package day25;
// 람다식 특징
 
// 문법
// (매개변수 목록) -> {실행문}
 
// 장점
// 1. 코드를 간결하게 만들 수 있다.
// 2. 코드가 간결하고 식에 개발자의 의도가 명확하게 드러나므로 가독성이 향상된다.
// 3. 함수(메서드)를 만드는 과정 없이 한 번에 처리할 수 있기에 코딩하는 시간이 줄어든다.
 
// 단점
// 1. 익명 함수는 재사용이 불가능하다. 
// 2. 디버깅이 다소 까다롭다.
// 3. 람다식을 남발하면 코드가 지저분해진다.(비슷한 코드의 중복 가능성 높음)
// 4. 재귀로 만들 경우에는 다소 부족한 면이 있다. (재귀함수 : 자신을 재참조하는 함수)
 
interface Caculator {
    int cal(int a, int b); // 두 수의 합을 구하는 메서드
}
 
public class LamdaEx2 {
    public static void main(String[] args) {
        
        Caculator c;
        
        c = (a, b) -> {return a + b; }; // 람다식
        
        System.out.println(c.cal(100200));
        
        
    }
    
    
}
 
cs

 

 

 

- 2개의 매개변수와 리턴 값이 없는 경우

 

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
package day25;
 
// 2개 매개변수와 리턴 값이 없는 경우
 
interface Calculate {
    void cal (int a, int b);
}
public class LamdaEx3 {
    public static void main(String[] args) {
        
        Calculate c;
        
        c = (a,b) -> {
            System.out.println("---------------------");
            System.out.println(a+b);
        };
        
        c.cal(43);
        
        c = (a, b) -> System.out.println(a-b);
//        if()
//            문장1;
//        위의 if문의 문장이 하나라 중괄호 생략한 것처럼, 람다식도 문장이 한 줄이면 중괄호 생략 가능
        
        c.cal(10050);
        
    }
}
 
cs

 

 

 

- 람다 표현식 : 다양한 람다식 표현방법

 

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
package day25;
 
interface Printable{
    void print(String msg);
}
 
 
public class LamdaEx4 {
    public static void main(String[] args) {
        
        Printable p;
        
        p = (String s) -> {
            System.out.println(s);
        };
        
        p.print("람다 표현식 1번 ");
        System.out.println("--------------------");
        
        
        // {}에 문장이 1개라면 {}를 생략 가능
        // 리턴값이 없을 때만 가능
        
        p = (String s) -> System.out.println(s);
        p.print("람다 표현식 2번 ");
        System.out.println("--------------------");
        
        
        // 매개변수의 정보가 있어서 타입을 추론(유추) 할 수 있다면
        // 자료형을 생략 가능
        
        p = (s) -> System.out.println(s);
        p.print("람다 표현식 3번 ");
        System.out.println("--------------------");
        
        
        
        // 매개변수가 1개만 존재한다면 ()도 생략할 수 있다.
        p = s -> System.out.println(s);
        p.print("람다 표현식 4번 ");
        System.out.println("--------------------");
        
        
    }
    
 
 
}
 
cs

 

 

- 익명 이너 클래스와 람다식

 

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
59
60
61
62
63
64
65
package day25;
 
// 익명 이너 클래스용
interface Merge{ // 인터페이스
    // 추상메서드
    public int add(int a, int b); 
    public int minus(int a, int b);
}
 
// 처음부터 람다식을 쓸 수 있는 인터페이스라는 것을 처음부터 정의할 수 있을까?
// 인터페이스에 @FunctionalInterface 추가
 
// 람다식용
@FunctionalInterface
interface Merge2{ // 인터페이스
    // 추상메서드
    public int add(int a, int b); 
//    public int minus(int a, int b); => 람다식은 메서드가 2개면 안돼서 생략
}
 
public class LamdaEx5 {
    public static void main(String[] args) {
        
        // 1. 익명 이너 클래스
        
        Merge m;
        
        m = new Merge() { // 미구현 메서드 추가
 
            @Override
            public int add(int a, int b) {
                return a+b;
            }
 
            @Override
            public int minus(int a, int b) {
                return a-b;
            }
            
            
        };
        
        System.out.println(m.add(100200));
        System.out.println(m.minus(100200));
        
        System.out.println("---------------------");
        
        
        // 2. 람다식
        // 람다식은 메서드 이름을 알기 때문에 안쓰는데,
        // 메서드가 2개라면 어떤 메서드인지 알 수 없음
        // 따라서, 인터페이스의 추상메서드가 단 하나여야만 람다식을 사용할 수 있다.
        
        Merge2 m2;
        
        m2 = (a, b) -> a+b;
        
        System.out.println(m2.add(200300));
        
    }
    
    
    
}
 
cs

 

 

 

- Generic을 활용한 람다식

 

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
package day25;
 
@FunctionalInterface // 람다식을 사용할 수 있는 인터페이스다.
interface MathUtil<T> { // 아직 타입(자료형) 미지정 : Generic
    T cal(T a, T b);
}
 
public class LamdaEx6 {
    public static void main(String[] args) {
        
        MathUtil<Integer> mu; // int
        
        mu = (a, b) -> a+b;
        
        System.out.println(mu.cal(100200)); // 300
        
        
        
        MathUtil<String> mus; // String
        
        mus = (a, b) -> a+b; // String은 더하면 '+'가 연결 연산자로 쓰임
        
        System.out.println(mus.cal("푸른하늘""은하수")); // 푸른하늘은하수
        
        
        
        MathUtil<Float> append; // Float
        
        append = (a, b) -> a+b;
        
        System.out.println(append.cal(300.0f, 200.0f)); // 500.0
        
        
    }
 
}
 
cs

 

 


 

- 1부터 100사이의 수를 랜덤하게 10개 뽑아 ArrayList에 담기 

 > 각각 기존 방법과 람다식으로..

 

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
package day25;
 
import java.util.ArrayList;
import java.util.Random;
 
public class LamdaEx7 {
    public static void main(String[] args) {
        
    
        // 1부터 100 사이의 수를 랜덤하게 10개를 뽑아서
        // ArrayList에 담기
        
        // list
        
        // ArrayList <== integer를 담는 전용 : list
        ArrayList<Integer> list = new ArrayList(); // Generic Integer
        
        // Random 객체 생성
        Random rnd = new Random();
        
        for(int i=0; i<10; i++) {
            list.add(rnd.nextInt(100)+1); // 1부터 100 사이의 정수값 구하기
        } // 10번 반복
        // nextInt() <-- 
        // arraylist에 담기
        
        System.out.println("list : "+list);
        
        
    }
    
    
}
 
cs

 

 

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
package day25;
 
import java.util.ArrayList;
import java.util.Random;
 
// 람다식 사용
@FunctionalInterface
interface MakeList<T>{
    public int make();
}
 
public class LamdaEx8 {
    public static void main(String[] args) {
        
        ArrayList<Integer> list = new ArrayList<Integer>();
        
        MakeList<Integer> mlist = () -> { // 매개변수 없음
            Random rnd = new Random();
            return rnd.nextInt(100)+1;
        };
        
        for(int i=0; i<10; i++) { // 리스트에 10개 집어넣기
            list.add(mlist.make());
        }
        
        System.out.println("list : "+list);
                
    }
}
 
cs

 


 

- java.util.function.Supplier 활용 > 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
package day25;
 
import java.util.ArrayList;
import java.util.Random;
import java.util.function.Supplier;
 
// 미리 정의되어 있는 인터페이스 사용 : 람다식
 
// java.util.function 밑에 있는 미리 정의되어 있는 함수적 인터페이스
 
public class LamdaEx9 {
    public static void main(String[] args) {
        
        ArrayList<Integer> list = new ArrayList<Integer>();
        
        Supplier<Integer> mlist = () -> { // Supplier
            Random rnd = new Random();
            return rnd.nextInt(100)+1;
        };
        
        for(int i=0; i<10; i++) { 
            list.add(mlist.get()); // get
        }
        
        System.out.println("list : "+list);
        
    }
}
 
cs

 

 

 

 

- java.util.function.Consumer;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package day25;
 
import java.util.function.Consumer;
 
public class LamdaEx10 {
    public static void main(String[] args) {
        
        Consumer<String> c = s -> System.out.println(s);
        
        c.accept("오늘은");
        c.accept("수요일");
        
    }
}
 
cs

 

 

- Supplier과 Consumer, Function. Predicate

 

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
59
60
61
62
63
64
65
package day25;
 
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
 
public class LamdaEx11 {
    public static void main(String[] args) {
        
        
        // Supplier<T>
        
        // 인자는 받지 않으며 리턴타입만 존재하는 메서드
        // 순수함수에서 결과를 바꾸는 것은 INPUT 뿐이다.
        
        Supplier<String> s = () -> "Hello Supplier";
        
        String result = s.get(); // 문자열에 넣고
        System.out.println(result); // 꺼내어 출력
        
        
        
        // Consumer<T>
        
        // 리턴을 하지 않고 (void) 매개변수를 받는 메서드를 갖고 있다.
        // 매개변수를 받아 소모한다는 의미
        
        Consumer<String> c = str -> System.out.println(str);
        c.accept("Hello Lamda");
        
        
        
        // Function<T, R>
        
        // 전형적인 함수를 지원
        // 하나의 매개변수와 리턴 타입을 갖는다.
        Function<String, Integer> f = 
                str -> Integer.parseInt(str); // String을 주면 int로 바꿔서 리턴
                
        Integer result2 = f.apply("1"); // 문자 1을 숫자 1로
        System.out.println(result2 + 100); // 1+100 = 101
        
        
        
        // Predicate<T>
        
        // 하나의 매개변수와 리턴타입을 갖는다.
        // 리턴타입을 지정하는 타입 파라미터가 안보인다.
        // 반환값은 Boolean
        // Function<T, Boolean> 형태와 동일
        
        Predicate<String> p = str -> str.isEmpty(); // 결과는 항상 boolean이라 따로 지정 x
        // isEmpty : 문자열이 텅텅 비었니? > 비었으면 true, 문자가 있으면 false 리턴
        
        boolean result3 = p.test("hello");
        System.out.println(result3);
        
        
        
        // 위의 항목들을 필요할 때 가져다 쓰기 ~.~
        
    }
}
 
cs