자바 13일차

하파타카·2021년 12월 15일
0

JAVA수업

목록 보기
10/10

한 일

  • 람다식
  • 매개변수 갯수에 따른 람다식
  • 멀티 스레드
  • 메소드 레퍼런스

람다식

인터페이스<타입> 객체명 = 매개변수 -> { 코드작성 };

  • 람다식을 사용하려면 함수형 인터페이스(functional interface)를 구현한다.
  • 함수형 인터페이스는 하나의 추상메서드만을 가진 인터페이스. (2개이상 가지면 안됨)
  • 코드가 한 줄 일때 코드블럭 {} 생략가능.

함수형 인터페이스 작성 예시

// 람다식을 쓰려면 functional interface (자바의 람다식은 함수형 인터페이스로만 사용한다)
@FunctionalInterface
public interface Test {
	void run();	// 추상 메서드 run을 생성 => 생성해주면 함수형 인터페이스 조건을 만족함
//	void run2();	// 함수형 인터페이스는 '하나'의 추상메서드만 가진 인터페이스임
}

메서드를 사용해 작성한 코드를 람다식으로 바꾼 예시

public class App {
	public static void main(String[] args) {
		// 문자열 array list 생성
		List<String> list = new ArrayList<>();
		list.add("하나");
		list.add("둘");
		list.add("셋");
		
		list.forEach(new Consumer<String>() {
		// Consumer 인터페이스(추상 메서드)를 익명 클래스에서 구현해야 함
			@Override
			public void accept(String t) {
				// 익명 클래스에서 구현
				System.out.print(t + "\t");
			}
		});
		System.out.println();
		
		// 익명 클래스를 람다식으로 변경
		list.forEach(t -> System.out.print(t + "\t"));
	}
}

매개변수가 없을 때의 람다식

매개변수 자리에 () 만 써줌. ()는 생략불가.

interface Runner {
	void execute();	// 추상메서드 1개 => 람다식 사용가능
}

public class App2 {
	public static void main(String[] args) {
		// Runner run = () -> {};	기본형태. 코드가 한줄이면 코드블럭{} 생략가능
		Runner run = () -> {
			System.out.println("헬로우!");
			System.out.println("람다식");
			};
		run.execute();
		
		System.out.println(run instanceof Runner);	// run은 Runner의 객체인가? 맞으면 true
		System.out.println(run.getClass());	// run의 클래스는 람다식
	}
}

매개변수가 하나일 때

()생략가능.

interface Runner {
	void execute(String t);	// 추상 메서드에 매개변수가 있는 경우
}

public class App {
	public static void main(String[] args) {
		// 매개변수가 있는 경우
		Runner run1 = (s) -> System.out.println(s);
		run1.execute("펭수");
		
		Runner run2 = x -> System.out.println(x);	// 매개변수가 1개일때 ()생략가능. 없을땐 생략불가이므로 주의
		run2.execute("길동");
	}
}

매개변수가 2개 이상일 때

매개변수 자리에 2개 이상을 차례로 작성하면 됨.

아래 예시의 (n, t) 가 매개변수임.

interface Runner {
	void execute(String name, String text);	// 추상 메서드에 매개변수가 있는 경우
}
public class App {
	public static void main(String[] args) {
		// 매개변수가 있는 경우
		Runner run1 = (n, t) -> System.out.printf("%s님 %s\n", n, t);
		run1.execute("펭수", "안녕하세요");
		
		greet(run1);	// 메소드로 사용
	}

	private static void greet(Runner run1) {
		run1.execute("길동", "건강하세요");
	}
}

리턴값이 있는 람다식

코드가 한 줄일때 return생략가능.

interface Joiner {
	String join(String text1, String text2);	// 추상 메서드에 리턴, 매개변수가 있는 경우
}

public class App {
	public static void main(String[] args) {
		// 리턴값이 있는 경우
		Joiner joiner = (t1, t2) -> {
			String text = t1 + " - " + t2;
			return text;
		};
		System.out.println(joiner.join("치킨", "맥주"));
		System.out.println(joiner.join("학원", "공부"));
		
		Joiner joiner2 = (s1, s2) -> s1 + " + " + s2;	// 코드가 한 줄일 때 return도 생략가능
		System.out.println(joiner2.join("학원", "공부"));
		System.out.println(joiner2.join("치킨", "맥주"));
	}
}

제네릭 타입과 람다식

  • predicate : 제넥릭 타입의 미리 지정된 함수형 인터페이스로, 입력된 타입의 객체를 검사해 true, false를 리턴. 조건을 지정할 때 사용.

removeIf() : true값을 반환한 원소를 제거.

public class RemoveIf {
	public static void main(String[] args) {
		// 특정 타입(제네릭)을 검사해서 참, 거짓을 리턴
		List<Integer> numbers = new ArrayList<>();
		numbers.add(3);
		numbers.add(5);
		numbers.add(7);
		numbers.add(2);
		numbers.add(9);
		numbers.add(10);
		numbers.add(4);
		numbers.forEach(t -> System.out.print(t + "\t"));	// numbers의 원소를 차례로 모두 출력
		System.out.printf("\n\n");
		// 리스트 numbers 안에 있는 정수들 중 6보다 작으면 모두 제거
		// removeIf(매개변수 -> 조건) : predicate로 검사(test)해서 참이면 다 제거
//		numbers.removeIf(new Predicate<Integer>() {
//			public boolean test(Integer i) {
//				return i < 6;	// 정수가 6보다 작으면 참
//			}
//		});
		// 23~27행을 이렇게 간단히 표현가능
		numbers.removeIf(i -> i < 6);
		numbers.forEach(x -> System.out.println(x));	// numbers의 원소를 차례로 모두 출력
		System.out.println();
		
		List<String> sList = new ArrayList<>();
		sList.add("하나둘");
		sList.add("둘셋넷");
		sList.add("셋둘하나");
		sList.add("여섯일곱여덟");
		sList.add("다섯넷셋");
		sList.add("하나둘셋");
		
		// 문자열의 길이가 4보다 작으면 모두 제거
		sList.removeIf(s -> s.length() < 4);	// 문자열의 길이가 4보다 작으면 참. 참이면 제거
		sList.forEach(s -> System.out.println(s));	// sList 원소 모두 출력
	}
}

ReplaceAll() : 각각의 값을 리턴된 값으로 변경

public class ReplaceAll {
	public static void main(String[] args) {
		// ReplaceAll : 각각의 값을 리턴된 값으로 바꿔줌
		List<Integer> numbers = new ArrayList<>();
		numbers.add(1);
		numbers.add(3);
		numbers.add(4);
		numbers.add(7);
		numbers.add(2);
		
		numbers.forEach(n -> System.out.print(n + "\t"));
		System.out.printf("\n\n");
//		numbers.replaceAll(new UnaryOperator<Integer> (){
//			public Integer apply(Integer n) {
//				return n * n;
//			}
//		});
		// 20~24행을 람다식으로 바꿔 줄임
		numbers.replaceAll(n -> n * n);
		numbers.forEach(n -> System.out.printf(n + "\t"));
		System.out.println();
		
		// 문자열의 경우
		List<String> friends = new ArrayList<>();
		friends.add("길동");
		friends.add("메리");
		friends.add("철수");
		friends.add("영식");
		friends.forEach(s -> System.out.println(s));
		friends.replaceAll(s -> "안녕~ " + s);
		friends.forEach(s -> System.out.println(s));

연습문제

public class App {
	public static void main(String[] args) {
		// 연습문제
		List<Integer> list = new ArrayList<>();
		list.add(1);
		list.add(5);
		list.add(9);
		list.add(1000);
		list.add(3);
		list.add(6);
		list.add(-20);
		list.add(4);
		// 1) 0 ~ 10이 아닌 값들은 모두 제거 
		
		System.out.printf("\n\n");
		// 2) 각 아이템들에 + 100
		
		// 3) 출력하여 확인
	}
}

풀어본것

public class App {
	public static void main(String[] args) {
		/* 연습문제
		 * 0 ~ 10이 아닌 값들은 모두 제거
		 * 그 다음, 각 아이템들에 +100을 한다
		 * 그 결과를 콘솔에 표시
		 */
		List<Integer> list = new ArrayList<>();
		list.add(1);
		list.add(5);
		list.add(9);
		list.add(1000);
		list.add(3);
		list.add(6);
		list.add(-20);
		list.add(4);
		// 1) 0 ~ 10이 아닌 값들은 모두 제거 
		list.removeIf(i -> (i <= 0 || i >= 10)); 	// true를 반환한 아이템을 삭제하므로 주의
		list.forEach(i -> System.out.print(i + "\t"));
		System.out.printf("\n\n");
		// 2) 각 아이템들에 + 100
		list.replaceAll(e -> e + 100);
		list.forEach(e -> System.out.print(e + "\t"));

	}
}

멀티 스레드

예제 1

public class App {
	public static void main(String[] args) {
		// 멀티 쓰레드
		Runnable runnable = () -> {
			for (int i = 0; i < 100; i++) {
				System.out.println("i : " + i);
				try {
					Thread.sleep(500);	// 0.5초 대기
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
		Thread t1 = new Thread(runnable);
		Thread t2 = new Thread(runnable);
		
		t1.start();
		t2.start();
		// t1과 t2가 동시에 한번씩 출력됨
	}
}


0.5초에 한 번씩 t1과 t2가 출력됨

예제 2

public class App {
	private int value = 0;
	public void run() throws InterruptedException {
		Runnable runnable = () -> {
			for (int i = 0; i < 200; i++) {
				value++;
			}
		};
		Thread t1 = new Thread(runnable);	// 새 스레드 t1 생성
		Thread t2 = new Thread(runnable);	// 새 스레드 t2 생성
		
		t1.start();	// t1을 실행
		t2.start();	// t2를 실행
		
		System.out.println("Value: " + value);
		// t1, t2 스레드의 실행보다 메인스레드의 실행이 빠르면 value의 값이 0 혹은 400보다 적게나옴
		// 고로 컴퓨터의 성능이 느릴 경우 메인스레드의 실행이 t1, t2가 실행된 후에 실행되면 0보다 큰 값이 나온다
		
		t1.join();	// 메인 스레드가 t1을 실행할때까지 대기
		t2.join();	// 메인 스레드가 t2을 실행할때까지 대기
		
		System.out.println("Value: " + value);	// t1, t2가 끝난 후 출력
	}
	
	public static void main(String[] args) throws InterruptedException {
		// 멀티 스레드2
		new App().run();	// 앱 객체를 만들고 run() 메소드 실행
	}
}

예제 3

public class App {
	
	private int value = 0;
	
	private synchronized void increment() {
		// synchronized : 해당 메소드에는 한번에 하나의 스레드만 접근할 수 있음.
		value++;
	}
	
	public void run() throws InterruptedException {
		Runnable runnable = () -> {
			for (int i = 0; i < 10000; i++) {
				increment();
			}
		};
		Thread t1 = new Thread(runnable);	// 새 스레드 t1 생성
		Thread t2 = new Thread(runnable);	// 새 스레드 t2 생성
		Thread t3 = new Thread(runnable);	// 새 스레드 t3 생성
		
		t1.start();	// t1을 실행
		t2.start();	// t2를 실행
		t3.start();	// t3를 실행
		
		System.out.println("Value: " + value);
		
		t1.join();	// 메인 스레드가 t1을 실행할때까지 대기
		t2.join();	// 메인 스레드가 t2을 실행할때까지 대기
		t3.join();	// 메인 스레드가 t2을 실행할때까지 대기
		
		System.out.println("Value: " + value);	// t1, t2가 끝난 후 출력
	}
	
	public static void main(String[] args) throws InterruptedException {
		// 멀티 스레드3
		new App().run();	// 앱 객체를 만들고 run() 메소드 실행
	}
}

메소드 레퍼런스

static 메소드를 만들어 사용해야 함
(static메소드는 객체생성없이 사용가능)
람다식과 서로 대체가능.

예제 1

public class App {
	public static void greet() {	// static메소드는 객체 생성없이 사용가능
		System.out.println("헬로우!");
	}
	
	public static void main(String[] args) {
		// 메소드 레퍼런스 : static메소드를 만들어 사용
		ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
		// 1초에 한번씩 헬로우! 출력
		service.scheduleAtFixedRate(()->System.out.println("헬로우!!"), 0, 1000, TimeUnit.MICROSECONDS);
//		service.scheduleAtFixedRate(App::greet, 0, 1000, TimeUnit.MICROSECONDS);
								// 클래스명::메소드명 => 메소드 레퍼런스
								// 람다식과 메소드 레퍼런스 모두 사용가능. 둘중 택1.
	}
}


1초에 한번씩 "헬로우!" 출력

예제 2

@FunctionalInterface
interface Greeter {
	void greet();
}

public class App2 {
	public static void main(String[] args) {
		Greeter g = () -> System.out.println("헬로우!");
		g.greet();

		Greeter g2 = App2::sayHello;
		g2.greet();
	}
	private static void sayHello() {
		System.out.println("헬로우!!");
	}
}

예제 3

public class App3 {
	public static void main(String[] args) {
		List<Integer> numbers = new ArrayList<>();
		numbers.add(1);
		numbers.add(6);
		numbers.add(4);
		numbers.add(7);
		numbers.add(3);
		numbers.add(6);
		numbers.add(3);
		
//		numbers.removeIf(n -> n<5);
		numbers.removeIf(App3::filter);	// 위의 람다식을 메소드 레퍼런스로 변경
		numbers.forEach(System.out::println);
		System.out.println();
//		numbers.replaceAll(n -> n *2);
		numbers.replaceAll(App3::map);	// 위의 람다식을 메소드 레퍼런스로 변경
		numbers.forEach(System.out::println);
		
	}
	private static boolean filter(int n) {
		return n < 5;
	}
	private static int map(int n) {	
		return n * 2; 
	}	
}

profile
천 리 길도 가나다라부터

0개의 댓글

관련 채용 정보