변화하는 요구사항
은 피할 수 없는 문제이다.효율적인 코드 설계
필요public class 변화하는요구사항대응하기 {
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (apple.getColor() == Color.GREEN) {
result.add(apple);
}
}
return result;
}
class Apple {
private Color color;
public Color getColor() {
return color;
}
}
enum Color {
RED, GREEN
}
}
public static List<Apple> filterRedApples(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (apple.getColor() == color) {
result.add(apple);
}
}
return result;
}
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (apple.getWeight() > weight) {
result.add(apple);
}
}
return result;
}
public static List<Apple> filterApplesByWeight(List<Apple> inventory, Predicate<Apple> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (predicate.test(apple)) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> inventories = List.of(
new Apple(Color.RED, 100),
new Apple(Color.GREEN, 200),
new Apple(Color.RED, 300),
new Apple(Color.GREEN, 400)
);
List<Apple> filteredApples = filterApplesByWeight(inventories, 200);
System.out.println(filteredApples);
}
[Apple{color=RED, weight=300}, Apple{color=GREEN, weight=400}]
prettyPrintApple
메서드 구현하기주어진 prettyPrintApple
메서드는 사과 리스트를 인수로 받아 각 사과에 대한 정보를 출력하는 기능을 가지고 있다. 이 메서드는 다양한 방식으로 사과의 정보를 출력할 수 있어야 하므로, 그 출력 방식을 파라미터화하는 것이 목적이다. 즉, 사과의 무게를 출력할 수도 있고, 사과가 무거운지 가벼운지 여부를 출력할 수도 있다.
이전 예제에서 사용했던 ApplePredicate 패턴과 유사하게, 출력을 제어하는 동작 파라미터화가 필요하다. 이를 위해 Function<Apple, String>
과 같은 인터페이스나 람다식을 활용하여 출력을 커스터마이징할 수 있다.
public static void main(String[] args) {
List<Apple> inventories = List.of(
new Apple(Color.RED, 100),
new Apple(Color.GREEN, 200),
new Apple(Color.RED, 300),
new Apple(Color.GREEN, 400)
);
prettyPrintApple(inventories, new ApplePrettyPrintFormatter());
}
public static void prettyPrintApple(List<Apple> inventories, AppleFormatter predicate) {
for (Apple apple : inventories) {
String message = predicate.accept(apple);
System.out.println(message);
}
}
public interface AppleFormatter {
String accept(Apple apple);
}
public static class ApplePrettyPrintFormatter implements AppleFormatter {
@Override
public String accept(Apple apple) {
String characteristic = apple.getWeight() > 150 ? "heavy" : "light";
return MessageFormat.format("{0} apple with {1} weight", apple.getColor(), characteristic);
}
}
//익명 클래스 사용하기
List<Apple> greenApples = filterApples(inventories, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return Color.RED == apple.getColor();
}
});
public class MeaningOfThis {
public final int value = 4;
public void doIt() {
int value = 6;
Runnable r = new Runnable() {
public final int value = 5; //
public void run() {
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
public static void main(String... args) {
MeaningOfThis m = new MeaningOfThis();
m.doIt();
}
}
자기 자신
을 보통 가리킨다.this
를 사용한다면 어떻게 될까?Runnable
인터페이스를 구현하는 익명 클래스가 선언되어 있다.this
는 익명 클래스를 가리키는 데value
라는 필드가 있고 5
로 설정run
메서드 실행시 this.value 는
익명클래스의 필드을 가리키기에 5
가 출력된다.this
는 바깥 클래스를 참조한다MeaningOfThis
클래스의 value
가 참조되어 4
가 출력된다.//람다식 사용하기
filterApples(inventories, apple -> Color.RED == apple.getColor())
.forEach(System.out::println);
public interface Predicate<T> {
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) { // 형식 파라미터
List<T> result = new ArrayList<>();
for (T t : list) {
if (predicate.test(t)) {
result.add(t);
}
}
return result;
}
//람다식 사용하기
filter(inventories, apple -> Color.RED == apple.getColor()).forEach(System.out::println);
List<Integer> evenNumbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
filter(evenNumbers, number -> number % 2 == 0).forEach(System.out::println);
Comparator
인터페이스를 사용해 정렬 동작 파라미터화 가능// 익명 클래스 사용해 무게 기준 정렬
inventories.sort(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight() - o2.getWeight();
}
});
// 람다 사용해 무게 기준 정렬
inventories.sort((o1, o2) -> o1.getWeight() - o2.getWeight());
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
t.start(); // 스레드를 시작
Thread t = new Thread(() -> System.out.println("Hello world"));
t.start(); // 스레드를 시작
Runnable
은 값 반환 X , Callable
은 작업이 끝난 후 결과
반환 가능// java.util.concurrent.Callable
public interface Callable<V> {
V call() throws Exception;
}
자바 5 부터 ExecutorService 등장
- ExecutorService
- 스레드 풀 관리 추상화 개념
- Runnable 을 통해 스레드 관리하는 것에서 한 단계 발전
- 스레드 풀에서 태스트 관리 + 실행이 간ㄷ나해짐
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> threadName = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
});
executorService.submit 에서 Callable
객체 실행
Future
객체가 반환된다.Future
는 비동기 작업의 결과를 담고 있고, 실제 작업이 완료된 이후에 결과를 가져온다.ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> threadName = executorService.submit(() -> Thread.currentThread().getName());