본문 바로가기
Programing Language/JAVA

Java 8 에서 추가된 기능

by doriver 2025. 4. 25.

 

Java 8 에서 추가된 기능

  • new Date and Time API (LocalDateTime 등)
  • Lambda, Stream API
  • Optional class
  • Interface Default Method

람다식(Lambda expression)

메서드를 하나의 식으로 표현한 것
코드가 간결해진다는 장점이 있지만, 람다식을 남용하면 오히려 코드를 이해하기 어려울 수 있다.

int min (int a, int b) {
    return a > b ? b : a;
}

// 람다식 방식
(a, b) -> a > b ? b : a;
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

// 람다식 방식
IntStream.range(0, 5).forEach((int value) -> System.out.println(value));

 

스트림 (Stream API)

스트림(Steam)은 컬렉션이나 배열 등과 같은 요소들의 집합들에 대한 처리를 보다 효과적으로 할 수 있도록 도와준다.

람다식을 지원한다는 점과 내부 반복자를 사용하기 때문에 병렬 처리가 쉽다는 특징이 있다.

 

스트림은 컬렉션이나 배열 등과 같은 요소들의 집합들로부터 생성될 수 있다.

String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);

 

Collection interface에 추가된 stream() 함수( 인터페이스에 추가된 default method로서, default method 역시 자바 8에 새로 추가된 기능 )를 통해 스트림을 생성한다. 

Collection interface에 추가되었기 때문에 Collection을 상속하는 클래스로 생성된 모든 객체들은 stream()을 통해 스트림을 생성할 수 있다.

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

 

스트림은 다양한 유용한 기능들을 제공하며 코드를 더 간결하게 나타낼 수 있게 도와준다

for (String a : list) {
    System.out.println(a);
}

list.stream().forEach(a -> System.out.println(a));

 

다양한 기능들을 람다식과 함께 제공

List<String> list = Arrays.asList("abc", "bac", "ccc", "ccc");

// a를 포함한 문자열이 있는지의 여부를 반환
boolean isExist = list.stream().anyMatch(element -> element.contains("a")); 
// a로 시작하는 문자열만 필터링
Stream<String> stringStream = list.stream().filter(element -> element.startsWith("a"));
// 문자열의 길이로 이루어진 스트림으로 변환
Stream<Integer> integerStream = list.stream().map(element -> element.length()); 
// 리스트 내의 중복 제거
Stream<String> distinctStream = list.stream().distinct();
// 멀티쓰레드를 이용한 병렬 처리. 메서드 참조도 사용
list.parallelStream().forEach(System.out::println);

 

지연된 연산

스트림 연산에서 중요한 점 중 하나는 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다는 것이다.

List<String> list = Arrays.asList("abc", "bac", "ccc", "ccc");
List<String> result = list.stream()
		.distinct().limit(5).sorted().collect(Collectors.toList());

distinct(), limit(), sorted(), collect() 라는 총 4가지의 함수를 호출했다. 

여기서 마지막 collect()를 제외한 나머지는 모두 '중간 연산'에 속하며 collect()라는 '최종 연산'이 호출되기 전까지는 아무것도 실행되지 않는다. (이는 Kotlin의 Sequence와 동일하다.) 

지연된 연산이 없었다면 매 연산이 실행될 때마다 새로운 객체를 생성했을 것이다. 아래는 스트림을 사용하지 않고 동일한 로직을 구현한다고 가정했을 때의 코드이다.

List<String> list = Arrays.asList("abc", "bac", "ccc", "ccc");

List<String> result = new ArrayList<>(new LinkedHashSet<>(list));

for (int i = 5; i < result.size(); i++) {
    result.remove(i);
}

result.sort(Comparator.naturalOrder());

 

번외

List<String> list = Arrays.asList("azanz", "berfinend", "fiel", "vom", "ferd");

// Stream으로 표현
list.stream()
    .filter(name -> name.startsWith("f"))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

 

 

인터페이스의 default 메서드와 static 메서드

자바8 이전의 interface는에 하나의 메서드를 추가하려면, 그 인터페이스를 구현한 모든 클래스에 그 메서드를 추가한 후 구현도 해줘야했다.

public interface TestInterface {
    public String method();
    public int multiply(int a, int b);

    default int add(int a, int b) {
        return a + b;
    }
    public static int minus(int a, int b) {
        return a - b;
    }
}

public class TestClass implements TestInterface{
    @Override
    public String method() {
        return "method";
    }
    @Override
    public int multiply(int a, int b) {
        return a * b;
    }
}

public class Main {
    public static void main(String[] args) {
        TestInterface testInterface = new TestClass();
        testInterface.add(1, 2);
        
        TestInterface.minus(2, 1);
    }
}

 

Optional

null이 올 수 있는 값을 감싸는 Wrapper클래스, NullPointerException이 발생하지 않도록 도와준다.
예상치 못한 NullPointerException이 발생될만한 상황에서 사용할 수 있다.

// 문자열로 Optional 객체 생성
Optional<String> optional = Optional.of("Hello world!");

if (optional.isPresent()) {
	String value = optional.get();
	System.out.println(value);    // 출력 결과 : Hello world!
}
		
// 비어있는 Optional 객체 생성
Optional<String> emptyOptional = Optional.empty();

String value = emptyOptional.orElse("default value");
System.out.println(value);    // 출력 결과 : default value
		
// 예외 발생
emptyOptional.orElseThrow(() -> new RuntimeException("throw Exception"));