본문 바로가기
Programming/Java

[ JAVA ] 익명 객체와 람다식

by 공부합시다홍아 2024. 2. 26.

익명 객체

인터페이스 타입으로 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우를 생각해보자.
인터페이스의 구현 클래스를 선언하고, 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