Programming/Java

[ JAVA ] 익명 객체와 람다식

공부합시다홍아 2024. 2. 26. 17:36

익명 객체

인터페이스 타입으로 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우를 생각해보자.
인터페이스의 구현 클래스를 선언하고, new 연산자를 이용해 구현 객체를 생성한 후, Field나 Local 변수에 대입하는 것이 기본이다. 

▶ 구현 클래스가 매번 달라지거나, 한 번만 사용되는 경우, 굳이 구현 클래스를 생성하지 않고 
     익명 클래스로(이름 없는 클래스)로 선언 할 수 있다.

☞ 멤버 변수 Car 에는 구현 클래스가 들어가야 한다.

Main Class

package api.ramda.basic;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class MainClass {

	public static void main(String[] args) {
		
		Person p = new Person();
		
		p.greeting( new Say01() {
			@Override
			public void talking() {
				System.out.println("hello");
			}
		});
		
		p.greeting( new Say01() {
			@Override
			public void talking() {
				System.out.println("안녕하세요");
			}
		});
		
		p.greeting( new Say01() {
			
			@Override
			public void talking() {
				System.out.println("니취팔로마");
			}
		});
		
		System.out.println("---------------------------------");
		
		//함수적인터페이스를 구현하는 익명객체를 -> 람다식으로 표현이 가능
		p.greeting( () -> { System.out.println("hello"); });
		p.greeting( () -> System.out.println("안녕하세요") 	  ); //표현할 코드가 한줄이면 {}생략이 됩니다
		
		
		System.out.println("---------------------------------");
//		String r = p.greeting( new Say02() {
//			@Override
//			public String talking() {
//				return "hello";
//			}
//		} );
		
		String r = p.greeting( () -> "hello" ); //한줄이면서, return이 생략되면 자동으로 return됩니다
		System.out.println(r);
		
		
		System.out.println("---------------------------------");
		
//		int r2 = p.greeting( new Say03() {
//			@Override
//			public int talking(String a, int b) {
//				//처리할 코드
//				int sum = 0;
//				for(int i = 1; i <= b; i++) {
//					sum += i;
//				}
//				return sum;
//			}
//		});
		
		
		int r2 = p.greeting( (a, b) -> { //자동타입추론 ()안에는 타입을 생략합니다. 매개변수가 1개라면 ()도 생략가능합니다
			
			int sum = 0;
			for(int i = 1; i <= b; i++) {
				sum += i;
			}
			return sum;
		});
		
		System.out.println(r2);
	
	}
}

Person Class

package api.ramda.basic;

public class Person {

	public void greeting(Say01 say) {
		say.talking();
	}
	
	//say02를 받는 인사법
	public String greeting(Say02 say) {
		String result = say.talking();
		return result;
	}
	
	//say03을 받는 인사법
	public int greeting(Say03 say) {
		
		int result = say.talking("hello", 10);
		return result;
	}
	
}

package api.ramda.basic;

//추상메서드가 1개인 인터페이스를 함수적인터페이스 라고 부릅니다.
public interface Say01 {
	public void talking();
}

//////////////////////////////////////////////////////////////////////

package api.ramda.basic;

public interface Say02 {
	public String talking();
}

//////////////////////////////////////////////////////////////////////

package api.ramda.basic;

public interface Say03 {

	public int talking(String a, int b);
}

람다식

함수적 인터페이스의 익명 구현 객체를 대신한다.( 자바의 람다식 )
먼저 람다식의 이해 이전에 함수적 프로그래밍과 근래 기법에 대해 간단히 알아본다.

함수적 프로그래밍

y = f(x) 형태의 함수로 구성된 프로그래밍 기법
▶ 함수를 매개값으로 전달하고 결과를 받는 코드로 구성

※ 함수적 프로그래밍이 객체 지향 프로그래밍보다 효율적인 경우
1. 대용량 데이터의 처리시
2. 이벤트 지향 프로그램 처리 시
최근 프로그래밍 기법
객체 지향 프로그래밍에 함수적 프로그래밍 기법을 추가한다. 

1. 자바 8부터 함수적 프로그래밍 기법 지원
    - 람다식(Lamda Expression)을 제공한다. 
    - 익명 함수를 생성한기 위한 식 : (매개변수, 매개변수) ▶ {실행문}

2. 람다식의 장점
    - 코드가 간결해진다.
    - 컬렉션 요소 처리가 쉬워진다.

람다식(Lambda Expression)은 코드블럭(Code Block)을 메소드에 넣을 때 사용하는 기술이다. 


람다식을 적용하기 위한 스트림

1. 반복자 스트림

  • 자바 8부터 추가된 Collection의 저장 요소를 하나씩 참조하도록 도와주는 반복자이다.
  • 람다식으로 처리할 수 있도록 해주는 반복자이다.
  • 파일 입출력 Stream과는 다른 개념이다. 
스트림의 특징
1. Iterorator과 비슷한 역할을 하는 반복자이다.
2. 대부분 메서드는 함수적 인터페이스 타입이다.
3. 속도면에서 빠르다.


  • Collection에서 Stream을 사용하는 간단한 예제

  • Stream은 중간처리와 최종처리가 함수형으로 표현 가능하다.

  • 중간 처리 메서드

  • 최종 처리 메서드

package api.ramda.basic2;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;

public class MainClass01 {

	public static void main(String[] args) {
		
		
		List<String> list = new ArrayList<>();
		list.add("홍길동");
		list.add("피카츄");
		list.add("라이츄");
		list.add("꼬북이");
		
		for(String s : list) {
			System.out.println(s);
		}
		
		System.out.println("-------------------------------------------");
		
		//Stream<String> stream = list.stream();
		//stream.forEach( a -> System.out.println(a) );
		
//		list.stream().forEach( new Consumer<String>() {
//			@Override
//			public void accept(String a) {
//				System.out.println(a);
//			}
//			
//		});
		
		list.stream().forEach( a -> System.out.println(a) );
		
	}
}


package api.ramda.basic2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class MainClass02 {

	public static void main(String[] args) {
		
		/*
		 * 펑셔널인터페이스
		 * Consumer - 매개변수가 1개 이상이고, 반환은 void
		 * Predicate - 매개변수가 1개 이상이고, 반환이 boolean
		 * Function - 매개변수가 1개 이상이고, 반환은 임의의 타입
		 */
		
		//100개의 랜덤한 값을 가지고 있는 리스트
		List<Integer> list = new ArrayList<>();
		
		for(int i = 0; i < 100; i++ ) {
			list.add( new Random().nextInt(100) + 1 );
		}
		System.out.println(list.toString());
		
		System.out.println("-----------------------------------");
		
		//중복제거
		list.stream().distinct().forEach( a -> System.out.print(a + " ") );
		
		System.out.println("\n-----------------------------------");
		
		//return에 true인 값들만 필터링해서 저장
		list.stream().filter( a -> a > 50 ).forEach(a -> System.out.print(a + " ") );
		
		System.out.println("\n-----------------------------------");
		
		//정렬
		list.stream().sorted().forEach(a -> System.out.print(a + " "));

		System.out.println("\n-----------------------------------");
		
		//map -> 메서드 안에 정의된 내용을 기준으로 새로운 리스트를 만듬
		//a는 리스트의 요소, 반환은 임의의 새로운값
		list.stream().map( a -> a % 2 == 0 ? true : false).forEach(a -> System.out.print(a + " ") );
		
		
		System.out.println("\n-----------------------------------");
		
		list.stream()
		.distinct()
		.map( a -> a > 50 ? a : 0 )
		.filter( a -> a != 0 )
		.sorted()
		.forEach( a -> System.out.print(a + " ") );
		
		System.out.println("\n-----------------------------------");
		
		//collect = 최종수집함수(새로운 list, set, map) 반환을 받을 수 있음
		List<Integer> newList = list.stream()
									.map( a -> a < 0 ? -a : a)
									.sorted()
									.collect( Collectors.toList() );
		
		System.out.println(newList);
		
		
		System.out.println("--------------------------------------");
		
		List<String> list2 = Arrays.asList("hong", "LEE", "Park", "Choi");
		
		//각 이름의 최초 0번째 글자를 얻어서, 전부 대문자로 치환하고, 알파벳순으로 정렬한 결과를 리스트로 반환받는다
		List<String> newList2 = list2.stream()
		.map( a -> a.toUpperCase().charAt(0) + "" )
		.sorted()
		.collect(Collectors.toList());
		
		System.out.println(newList2);
		
		
		Map<String, Integer> map = list2.stream().collect(Collectors.toMap(a -> a, a -> a.length() ));	
		System.out.println(map.toString());
		
	}
}


package api.ramda.basic2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.BinaryOperator;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class MainClass03 {

	public static void main(String[] args) {
		
		/*
		 * Stream의 타입
		 * 
		 * Stream - 오리지널스트림
		 * IntStream - 정수저장스트림
		 * DoubleStream - 실수저장스트림
		 * LongStream - 롱타입 저장스트림
		 * Optional - 이외의 것들을 저장스트림
		 * 
		 */
		List<Integer> list = new ArrayList<>();
		
		for(int i = 1; i <= 20; i++) {
			list.add( new Random().nextInt(100) + 1  );
		}
		
		System.out.println(list.toString());
		
		//평균, 합계, 개수 등 집계기능을 사용하려면 정수형스트림으로 변경
		//mapToXXX() 
		long cnt = list.stream().mapToInt( a -> a).count(); //count -최종함수
		System.out.println("데이터개수:" + cnt);
		
		int sum = list.stream().mapToInt(a -> a).sum(); //sum - 최종함수
		System.out.println("데이터의합계:" + sum);
		
		//optionalXX은 특정한 값을 저장하고 있는 객체, 값을 얻을때는 get메서드를 사용함
		double avg = list.stream().mapToInt(a -> a).average().getAsDouble();
		System.out.println("데이터의평균:" + avg);
		
		int min = list.stream().mapToInt(a -> a).min().getAsInt();
		System.out.println("최소값:" + min);
		
		//boxed() -> 정수스트림을 오리지널스트림으로 형변환
		Stream<Integer> s = list.stream().mapToInt(a -> a).boxed();
	
		//Reduce() - 사용자가 정의한 합계를 구할 수 있는 최종메서드
		List<String> list2 = Arrays.asList("a", "b", "c", "d", "e");
		//a가 초기값, b가 요소
		String result = list2.stream().reduce((a, b) -> a+b).get();
		System.out.println(result);
		
		System.out.println("---------------------------------------------------");
		
		//정수스트림을 빠르게 만드는법
		//0~10미만 값을 인트스트림으로 생성
		IntStream.range(0, 10).forEach( a -> System.out.print(a + " ") );
		
		System.out.println();
		
		//0~10까지 값을 인트스트림으로 생성
		IntStream.rangeClosed(0, 10).forEach( a -> System.out.print(a + " "));
		
		System.out.println();
		
		//1~100까지 값을 저장하는 리스트
		List<Integer> list3 = IntStream.rangeClosed(1, 100).boxed().collect( Collectors.toList()  );
		System.out.println(list3);

	}
}

728x90