본문 바로가기

Java/문법

18 - 2 장 람다식의 활용

1. 구현 메서드의 약식 표현

 

- 함수형 인터페이스의 객체 생성 과정에서 익명 이너 클래스를 이용한 객체 생성 방식의 축약된 표현을 제공

- 직접 추상 메서드를 구현하는 형태 

- 익명 이너 클래스 내부의 구현 메서드를 단순히 축약해 표현한 형태 

 

package OOP;

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 mehtod4(int a, double b);
}
 class OOP {
	 public static void main(String[] args) {
		 //인터페이스의 함수 구현 -> 람다식 
		 //1.입력 x, 리턴 x
		 //1-1 익명 이너 클래스 방식
		 A a1 = new A() {
			 //override
			 public void method1() {
				 System.out.println("입력 x,리턴 x 함수");
			 }
		 };
		 //1-2 람다식 표현 
		 A a2 = () -> System.out.println("입력 x, 리턴 x 함수");
		 
		 //2. 입력 o, 리턴 x
		 //2-1 익명 이너 클래스 방식
		 B b1 = new B() {
			 //override
			 public void method2(int a) {
				 System.out.println("입력 o, 리턴 x함수");
			 }
		 };
		 //2-2 람다식 표현 
		 B b2 = a -> System.out.println("입력 o, 리턴 x함수");
		 
		 //3. 입력 x, 리턴 o
		 //3-1 익명 이너 클래스 방식
		 C c1 = new C() {
			 //override
			 public int method3() {
				 return 4;
			 }
		 };
		 //3-2 람다식 표현
		 C c2 = ()-> 4;
		 
		 //4.입력 o, 리턴 o
		 //4-1 익명 이너 클래스 방식 
		 D d1 = new D() {
			 //override
			 public double method4(int a, double b) {
				 return a+b;
			 }
		 };
		 //4-2 람다식 표현
		 D d2 = (a,b) -> a+b;
	 }
}

 

2. 메서드 참조 

 

- 추상 메서드를 직접 구현하는 대신, 이미 구현이 완료된 메서드를 참조

- 인스턴스 메서드 참조 / 정적 메서드 참조 

 

1) 정의돼 있는 인스턴스 메서드 참조 

 

- 인스턴스 메서드를 참조하기 이해서는 먼저 인스턴스 메서드가 사용할 수 있는 상태가 돼야 하므로 객체 먼저 생성

- 객체 생성 후 '객체 참조 변수 :: 인스턴스 메서드명' 작성

클래스 객체 :: 인스턴스 메서드명

 

ex) 

-abc() 추상 메서드 하나를 갖는 함수형 인터페이스 A와 인스턴스 메서드 bcd()를 갖고 있는 B클래스 정의 

 

-구현한 abc() 메서드에는 B 클래스 객체를 생성해 인스턴스 메서드 bcd()를 호출 

- abc() 메서드를 호출하면 B 객체 멤버인 bcd()를 호출하는 것

 -> abc()는 bcd()와 동일

 

- bcd()는 이미 완성된 인스턴스 메서드이므로 abc()가 bcd()와 동일하다는 사실만 알려주면 됨

 -> 람다식의 인스턴스 메서드 참조

B b = new B();
A a = b::bcd;

 -> 'A 인터페이스 내부의 abc() 메서드는 참조 변수 b 객체 내부의 인스턴스 메서드 bcd()와 동일하다'

 

-abc()가 bcd()를 참조하기 이해서는 리턴 타입과 매개변수의 타입이 반드시 동일해야함

 

package OOP;

interface A {
	void abc();
}

class B{
	void bcd() {
		System.out.println("메서드");
	}
}
 class OOP {
	 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) 자바가 제공하는 인스턴스 메서드 참조 

 

- 내부에 void abc(int k) 추상 메서드를 가진 함수형 인터페이스 A 

- 람다식 및 익명 이너 클래스로 구현한 abc() 메서드는 입력매개변수를 그대로 전달해 System.out.println()만을 호출

 -> abc()는 System.out.println()와 항상 동일한 동작을 수행

 

- System.out 자체가 객체이므로 다음과 같이 간단하게 작성 가능

A a = System.out :: println;

 

- '인터페이스 A의 추상 메서드인 abc()는 System.out.println()을 참조하라'

-abc()를 호출하면 대신 System.out.println()을 호출하라 

package OOP;

interface A {
	void abc(int k);
}

public class Ramda {
	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);
		a2.abc(3);
		a3.abc(3);
	}
}

 

 

3) 정의돼 있는 정적 메서드 참조 

 

-'클래스명 :: 정적 메서드명'으로 메서드 참조 

클래스명 :: 정적 메서드명

- 정적 메서드는 객체 생성 없이 클래스명으로 바로 사용 가능 

- 정적 메서드의 참조를 위해서는 리턴 타입과 입력매개변수 타입이 동일해야함 

 

ex) 

-abc() 추상 메서드 1개를 갖는 함수형 인터페이스 A

- 정적 메서드 bcd()를 갖고 있는 B 클래스 정의 

 

- 익명 이너 클래스 및 람다식을 이용해 인터 페이스 A 객체 생성 후 abc() 메서드 구현 

- 구현한 abc() 메서드 내부에서는 B.bcd()와 같이 클래스 B의 정적 메서드만을 호출

 -> abc() 메서드가 B.bcd()와 동일한 기능 수행

 

A a = B::bcd;

 - '인터페이스 A의 객체를 생성할 때 구현해야 하는 abc() 메서드를 B.bcd()와 동일하게 하라'

 

package OOP;

interface A {
	void abc();
}

class B {
	static void bcd() {
		System.out.println("메서드");
	}
}

public class Ramda {
	public static void main(String[] args) {
		//1.익명 이너 클래스 
		A a1 = new A() {
			//override
			public void abc() {
				B.bcd();
			}
		};
		
		//2.람다식으로 표현
		A a2 = ()-> {B.bcd();};
		
		//3.정적 메서드 참조 
		A a3 = B::bcd;
		a1.abc();
		a2.abc();
		a3.abc();
	}
}

 

 

4) 첫 번째 매개변수로 전달된 객체의 인스턴스 메서드 참조 

 

- 추상 메서드의 구현 과정에서 첫 번째 매개변수로 인스턴스 메서드를 포함하고 있는 객체를 함께 전달, 이후 전달된 객체의 인스턴스 메서드를 참조 

- 객체가 첫 번째 매개변수로 전달되므로 따로 객체생성하는 과정은 필요 없음

클래스명 :: 인스턴스 메서드명

 -> 정적 메서드와 참조 형태 같음 But , static 유무로 구별 가능

 -> 정적 메서드 참조할 때는 추상 메서드와 참조 메서드의 입력 매개변수가 모두 동일

 -> 전달된 객체의 메서들르 참조할 때는 추상메서드의 첫 번째 매개변수로 객체가 들어가기 때문에 참조 메서드보다 매개변수가 1개 더 많음

 

ex)

- abc(B b, int k)와 같이 2개의 매개변수를 입력받는 추상메서드를 포함한 인터페이스 A

- bcd(int k)와 같이 하나의 매개변수를 갖는 인스턴스 메서드를 포함한 클래스 B

 

- 익명 이너 클래스 및 람다식으로 인터페이스 A의 객체를 생성하는 과정에서 구현한 abc() 메서드에는 

첫번째 매개변수로 넘어온 객체의 bcd() 메서드를 호출함

 

A a = B::bcd;

- abc(B b, int k)의 메서드는 첫 번째 매개변수 내부의 bcd() 메서드와 동일 

 

package OOP;

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

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

public class Ramda {
	public static void main(String[] args) {
		//1.익명 이너 클래스 
		A a1 = new A() {
			//override
			public void abc(B b, int k) {
				b.bcd(k);
			}
		};
		
		//2.람다식으로 표현
		A a2 = (B b, int k)-> {b.bcd(k);};
		
		//3.정적 메서드 참조 
		A a3 = B::bcd;
		a1.abc(new B(), 3);
		a2.abc(new B(), 3);
		a3.abc(new B(), 3);
	}
}

 

 

5) 자바가 제공하는 인스턴스 메서드 참조 

package OOP;

interface A {
	int abc(String str);
}

public class Ramda {
	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) -> str.length();
		
		//3.자바가 제공하는 인스턴스 메서드 참조 
		A a3 = String :: length;
		System.out.println(a1.abc("안녕"));
		System.out.println(a2.abc("안녕"));
		System.out.println(a3.abc("안녕"));
	}
}

 

 

3. 생성자 참조 

 

1) 배열 생성자 참조 

 

 - 함수형 인터페이스에 포함된 추상 메서드가 배열의 크기를 입력매개변수로 하며,  특정 배열 타입을 리턴한다면 구현 메서드의 내부에는 반드시 new 자료형[]이 포함될 것임

 

- 추상 메서드의 구현 메서드가 new 자료형[]과 같이 배열 객체의 생성 기능만을 수행할 때는 람다식의 배열 생성자 참조 방법을 사용 가능

배열 타입 :: new

 

ex)

- 정수 1개를 입력매개변수로 갖고, int[] 배열 타입을 리턴하는 추상메서드를 포함하는 인터페이스 A

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

-인터페이스 A의 객체를 익명 이너 클래스 및 람다식으로 작성

 

익명이너클래스 

A a = new A() {
	public int[] abc(int len) {
    	return new int[len];
    }
};

 

람다식

A a = (len) -> new int[len];

 

 -> new int[len]과 같이 new 키워드로 입력 매개변수로 전달받은 정수의 크기를 갖는 배열 객체만을 생성한 후 리턴 

 

- 구현 메서드의 내용이 단순히 배열 객체를 생성할 때는 람다식의 배열 생성자 참조 방법 사용 가능

A a = int[] ::new;

 -> 'abc(len)를 호출하면 len 크기를 갖는 int[]를 생성하는 new int[len]을 실행한 후 객체를 리턴하라'

 

 

2) 클래스 생성자 참조 

 

- 인터페이스의 추상 메서드가 클래스 타입의 객체를 리턴할 때도 생성자 참조 사용 가능

- 추상 메서드를 구현할 때 new 생성자()와 같이 객체만을 생성해 리턴하면 클래스 생성자 참조 사용 가능

 

ex)

 - 인터페이스 A는 B 타입을 리턴하는 추상 메서드 abc()를 갖고 있음

 - 클래스 B에는 기본 생성자를 포함해  2개의 생성자가 정의

interface A {
	B abc();
}

class B{
	B(){} //첫번째 생성자 
    B(int k){} //두번째 생성자
}

 

- 인터페이스 A의 객체를 익명 이너 클래스와 람다식의 방법으로 작성

 

익명 이너 클래스 

A a = new A() {
	public B abc() {
    	return new B();
    }
};

 

람다식

A a = () -> new B();

- > 객체를 생성할 때 구현한 abc() 메서드의 내부에는 new B()와 같이 클래스 B의 객체만을 생성해 리턴

 

- 클래스의 new 생성자를 참조할때

A a = B::new;

 -> 'a.abc() 메서드를 호출하면 new B()를 실행해 객체를 생성하라'

 

- 생성자가 여러개일때 기본생성자와 다른 생성자를 호출해 객체 생성하는 방법

 -> 인터페이스 A에 포함된 추상메서드의 매개변수에 따라 결정

 

interface A {
 	B abc(int k);
}

class B{
	B(){} // 첫 번째 생성자 
    B(int k) {} // 두 번째 생성자 
}

익명 이너 클래스로 인터페이스 A 객체 생성

A a = new A() {
	public B abc(int k) {
    	return new B(k);
     }
 };

람다식으로 인터페이스 A 객체 생성

A a = (k) -> new B(k);

- >구현 메서드로 넘어온 매개변수를 그대로 입력매개변수로 갖는 생성자를 이용해 객체 생성

 

A a = B::new;

 -> 'abc(k)를 호출하면 new B(k)를 실행하라'