티스토리 뷰

수업/└Java

[CH17]람다식

onlyun 2022. 2. 10. 09:29

-람다식이란?
-객체지향형 프로그래밍의 메소드 사용법 : 인터페이스 구현, 익명이너클래스, 람다식
-람다식 활용 : 익명클래스 구현 메소드 약식표현, 메소드 참조, 생성자 참조

 

1. 람다식이란?

함수형 프로그래밍. 함수의 구현과 호출만으로 프로그램을 만들 수 있는 것.

자바 제공하는 함수형 프로그래밍 방식 '람다식'. 함수 이름없는 익명 함수.

코드 간결화 및 병렬처리에 강함. 컬렉션 API 성능 효과적 개선(Stream)

 

함수적 프로그래밍 vs 객체지향형

-함수 : 독립적으로 사용. 기능, 동작 정의 

void abc(){ /*기능, 동작*/ }

-메소드 : 클래스 또는 인터페이스 내부에 정의된 함수. 객체 지향에서 사용.

class A{
	void abc(){ /*기능, 동작*/ }

-함수형 인터페이스 : 내부에 단 1개의 추상 메소드만 존재

interface A{
	public abstract void abc(); //추상 메소드
}

 

→ 함수형 인터페이스만 람다식 표현 가능

 

 

2. 객체지향형 프로그래밍의 메소드 사용

함수를 호출하는 방식은 모두 같음.

(1) 인터페이스 구현한 클래스 이용

인터페이스 A를 구현한 클래스를 만들어 객체 생성 후 메소드 사용. - 객체지향형 프로그래밍 문법1

package lambda;

interface A {
	void abc(); //추상메소드
}
class B implements A {
	@Override
    public void abc(){
    	System.out.println("Method 1");
    }
}
public class OOPvsFP {
	public static void main(String[] args){
    	A a1 = new B();
        a1.abc();  //Method 1
    }
}

 

(2) 익명이너클래스 사용

package lambda;

interface A {
	void abc(); //추상메소드
}
class B implements A {
	@Override
    public void abc(){
    	System.out.println("Method 1");
    }
}
public class OOPvsFP {
	public static void main(String[] args){
    	//익명이너클래스
        A a2 = new B(){
        	@Override
            public void abc(){
            	System.out.println("Method 2 익명이너클래스");
            }
        };
        //호출
        a2.abc();  //Method 2 익명이너클래스
    }
}

 

(3) 람다식 사용

-람다식 = 익명이너클래스 약식 표현.

package lambda;

interface A {
	void abc(); //추상메소드
}
class B implements A {
	@Override
    public void abc(){
    	System.out.println("Method 1");
    }
}
public class OOPvsFP {
	public static void main(String[] args){
    	//람다식
        A a3 = () -> {
        	System.out.println("Method 3 람다식");
		};
        //호출
        a3.abc();
    }
}

-표현 한꺼번에 모아보기

더보기
package lambda;

interface A {
	void abc();  //인터페이스
}
class B implements A{
	 //인터페이스는 객체생성 불가. 구현한 클래스를 통해 객체 생성
	@Override
	public void abc() {
		System.out.println("Method 1");
	}
}
public class OOPvsFP {
	public static void main(String[] args) {
		//1. 객체지향 프로그래밍 문법1
		A a1 = new B();
		a1.abc(); //Method 1
		
		//2. 객체지향 프로그래밍 문법2(익명이너클래스)
		A a2 = new B() {
			@Override
			public void abc() {
				System.out.println("Method 2 - 익명이너클래스");
			}
		};
		a2.abc(); //Method 2 - 익명이너클래스
		
		//3. 람다식
		A a3 = () -> {
			System.out.println("Method 3 - lmabda");
		};
		//3-1. 람다식 축약 표현
        //A a3 = () -> System.out.println("Method 3 - lmabda");
		a3.abc();  //Method 3 - lmabda
	}//main
}

 

(4) 람다식 약식 표현

-생략할 수 있는 것 : 중괄호, 리턴(return), 매개변수 타입(소괄호 반드시 생략)

소괄호 생략 시 매개변수 타입은 반드시 생략.

리턴(return) 생략 시 중괄호 반드시 생략.

//1. 중괄호 생략 : 실행문 하나
A a = () -> {System.out.println(“테스트”);};
A a = () -> System.out.println(“테스트”);

//2. 매개변수 타입 생략. 소괄호 생략
A a = (int a) -> {  };
A a = a {  };

//3. 리턴 생략 : 실행문이 리턴만 있을 경우, 리턴 생략 시 중괄호 반드시 생략
A a = (a, b) -> {return a+b;};
A a = (a, b) -> a+b;

3. 람다식 활용

익명이너클래스 메소드 참조 생성자 참조
  인스턴스 메소드 참조1, 2
정적 메소드 참조
배열 생성자 참조,
클래스 생성자 참조
  클래스객체::인스턴스메소드이름
클래스이름::정적메소드이름
클래스이름::인스턴스메소드이름
배열타입::new

 

(1) 익명이너클래스 내 구현 메소드의 약식 표현

익명이너클래스 내 구현 메소드를 람다식(약식)으로 표현. 이때, 함수형 인터페이스(내부에 한 개의 메소드만 포함한 인터페이스)만 가능. 왜냐하면 람다식은 메소드 이름을 생략하기 때문에 함수가 여러 개 있을 경우에는 무엇을 호출하는지 알 수 없게 되기 때문에 인터페이스 내에 추상 메소드가 여러 개일 경우 사용 불가.

-입력(매개변수) 여부, 리턴 여부에 따라 익명이너클래스 방식, 람다식 방식 표현 예시) ↓

package lambda;
//함수형 인터페이스의 객체를 생성하기 위한 람다식 표현 방법
interface A{//입력 x, 리턴x
	void method1();
}
interface B{//입력 o, 리턴x
	void method2(int a);
}
interface C{//입력 x, 리턴o
	int method3();
	
}
interface D{//입력 o, 리턴o
	double method4(int a, double b);	
}

public class FunctionToLambdaExpression {
	public static void main(String[] args) {
	//인터페이스 함수 구현 -> 람다식
	//1. 입력 x, 리턴x
	//1-1. 익명이너클래스
	A a1 = new A() {
		@Override
		public void method1() {
			System.out.println("func - 입력 x, 리턴x");
		}
	};
	//1-2 람다식
	A a2 = () -> { System.out.println("func - 입력 x, 리턴x");};
	A a3 = () -> System.out.println("func - 입력 x, 리턴x");
	
	//2 입력 o, 리턴x
	//2-1.익명이너클래스
	B b1 = new B() {
		public void method2(int a) {
			System.out.println("func - 입력 o, 리턴x");
		}
	};
	//2-2 람다
	B b2 = (int a) -> {System.out.println("func - 입력 o, 리턴x");};
	B b3 = (int a) -> System.out.println("func - 입력 o, 리턴x");  //중괄호 생략
	B b4 = (a) -> System.out.println("func - 입력 o, 리턴x");  //입력매개변수 생략가능
	B b5 = a -> System.out.println("func - 입력 o, 리턴x");  //입력매개변수 1개, 소괄호 생략
	
	//3.입력 x, 리턴o
	//3-1. 익명
	C c1 = new C() {
		public int method3() {
			return 4;
		}
	};
	//3-2 람다
	C c2 = () -> {return 4;};
	C c3 = () -> 4; //리턴 생략시 중괄호 함께 생략
	
	//4.입력 o, 리턴o
	//4-1.익명이너클래스
	D d1 = new D() {
		public double method4(int a, double b) {
			return a+b;
		}
	};
	//4-2 람다
	D d2 = (int a, double b) -> { return a+b; };
	D d3 = (a, b) -> { return a+b; };  //매기변수 타입 생략
	D d4 = (a, b) -> a+b; //리턴 생략
	}//main
}

 

(2) 메소드 참조

이미 정의된
인스턴스 메소드 참조
정적 메소드 참조 이미 정의된
인스턴스 메소드 참조
클래스객체::인스턴스메소드이름 클래스이름::정적메소드이름 클래스이름::인스턴스메서드이름

 

(2-1) 이미 정의된 인스턴스 메소드 참조  클래스객체::인스턴스메소드이름

리턴타입과 매개변수 동일

package lambda;

interface A {
	void abc();
}

class B {  
	void bcd() {  //이미 정의된 인스턴스 메소드
		System.out.println("Method");
	}
}

public class RefOfInstanceMethod_Type2_1 {
	public static void main(String[] args) {
		//1 익명이너클래스
		A a1 = new A() {
			@Override
			public void abc() {
				B b = new B(); //(익명이너클래스 내부 객체 생성)
				b.bcd();
			}
		};
		
		//2 람다식
		A a2 = () -> {
			B b = new B();
			b.bcd();
		};
		
		//3 이미 정의된 인스턴스 참조
		B b = new B();
		A a3 = b::bcd;
		
		//출력
		a1.abc();
		a2.abc();
		a3.abc();
	}
}

(2-1_2) 이미 정의된 인스턴스 메소드 참조

매개변수가 있는 경우

package lambda;

interface A {
	void abc(int k);
}

public class RefOfInstanceMethod_Type2_2 {

	public static void main(String[] args) {
		//1. 익명
		A a1 = new A(){
			@Override
			public void abc(int k) {
				System.out.println(k);
			}
		};
		
		//2 람다
		A a2 = (int k) -> System.out.println(k);
		
		//3 인스턴스메소드 참조
		A a3 = System.out::println; //클래스 객체::인스턴스메소드 이름
		
		a1.abc(3);  //3
		a1.abc(3);  //3
		a1.abc(3);  //3

	}//main
}

 

(2-2) 정적 메소드 참조  클래스이름::정적메소드이름

package lambda;

interface A{
	void abc();
}

class B{
	static void bcd() {
		System.out.println("static metohd");
	}
}

public class RefOfStaticMethod_Type {
	public static void main(String[] args) {
		//1.익명이너클래스
		A a1 = new A() {
			@Override
			public void abc() {
				//static method 객체 생성 필요없이 바로 접근
				B.bcd();
			}
		};
		//2 람다
		A a2 = () -> B.bcd();
		
		//3 정적 메소드 참조  클래스메소드이름::정적메소드이름
		A a3 = B::bcd;
		
		//호출
		a1.abc();  //static metohd
		a2.abc();  //static metohd
		a3.abc();  //static metohd
	}
}

 

(2-3) 인스턴스 메소드 참조 2  클래스이름::인스턴스메서드이름

첫번째 매개변수로 전달된 객체의 메소드를 참조 

package lambda;

interface A{
	void abc(B b, int k);
}

class B{
	void bcd(int k) {
		System.out.println(k);
	}
}

public class RefOfInstanceMethod_Type2_3_1 {

	public static void main(String[] args) {
		//익명이너클래스
		A a1 = new A() {
			@Override
			public void abc(B b, int k) {
				b.bcd(k);
			}
		};
		//람다식
		A a2 = (B b, int k) -> b.bcd(k);
		
		//직접 정의한 인스턴스메소드 참조
		A a3 = B::bcd;
		
		//호출 //(클래스 객체, int k)
		a1.abc(new B(), 3); //3 
		a2.abc(new B(), 4); //4
		a3.abc(new B(), 5); //5
	}

}

*자바가 제공하는 인스턴스 메소드 참조(String str)

package lambda;

interface A {
	int abc(String str);
}

public class RefOfInstanceMethod_Type2_3_2 {
	public static void main(String[] args) {
		//1익명이너클래스
		A a1 = new A() {
			@Override
			public int abc(String str) {
				return str.length();
			}
		};
		//2 람다
		A a2 = (String str) -> { return str.length(); };
		A a3 = (String str) -> str.length();  //축약
		
		//3. 자바가 제공하는 인스턴스 메소드 참조 매개변수 클래스이름::메소드이름
		A a4 = String::length;
		System.out.println(a1.abc("hello"));  //5
		System.out.println(a2.abc("hello"));  //5
		System.out.println(a4.abc("hello"));  //5
	}
}

 

(3) 생성자 참조

(3-1) 배열 생성자 참조

배열 생성자 참조, 클래스 생성자 참조  배열타입::new

interface 메소드의 리턴타입 = 배열 객체

배열 생성자 참조를 위해서는 인터페이스 메소드의 매개변수로 배열의 길이 전달.

package lambda;

interface A{
	int[] abc(int len);
}

public class RefOfArrayConstructor {
	public static void main(String[] args) {
		// 1.익명이너클래스
		A a1 = new A() {
			@Override
			public int[] abc(int len) {
				return new int[len]; //배열 객체 생설할 때 크기 지정. 매개변수로
			}
		};
		
		//2람다
		A a2 = (int len) ->	{ return new int[len]; };

		//3 배열 생성자 참조
		A a3 = int[]::new;
		
		// 생성? 출력
		int[] array1 = a1.abc(3);
		System.out.println(array1.length);  //3
		int[] array2 = a2.abc(3);
		System.out.println(array2.length);  //3
		int[] array3 = a3.abc(3);
		System.out.println(array3.length);  //3
	}
}

 

(3-2) 클래스 생성자 참조  클래스이름::new

interface 메서드의 리턴타입=클래스 객체

-매개변수 없는 경우

package lambda;
interface A{
	B abc();
}
class B {
	B(){
		System.out.println("1st 생성자");
	}
	B(int k){
		System.out.println("2nd 생성자");
	}
}
public class RefOfArrayConstructor_1 {
	public static void main(String[] args) {
		//1익명이너클래스_1
		A a1 = new A() {
			@Override
			public B abc() {
				return new B();
			}
		};

		//람다
		A a2 = () -> { return new B(); };
		A a3 = () -> new B();
		
		//3 클래스 생성자
		A a4 = B::new;
		
		a1.abc();  //1st 생성자
		a2.abc();  //1st 생성자
		a4.abc();  //1st 생성자
	}

}

-매개변수 있을 경우

package lambda;
interface A{
	B abc(int k);
}
class B {
	B(){
		System.out.println("1st 생성자");
	}
	B(int k){
		System.out.println("2nd 생성자");
	}
}

public class RefOfArrayConstructor_2 {
	public static void main(String[] args) {
		//1익명이너클래스_1
		A a1 = new A() {
			@Override
			public B abc(int k) {
				return new B(k);
			}
		};

		//람다
		A a2 = (int k) -> { return new B(k); };
		A a3 = (int k) -> new B(k);
		
		//3 클래스 생성자
		A a4 = B::new;
		
		a1.abc(3);  //2nd 생성자
		a2.abc(3);  //2nd 생성자
		a4.abc(3);  //2nd 생성자
	}
}

 

End

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
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
글 보관함