Lambda (람다 표현식)
익명 함수를 단순하고 간결하게 작성할 수 있도록 도와준다. 이는 가독성을 높이고, 함수형 인터페이스와 함께 사용할 때 유용하다.
// 기존 익명 클래스 방식
Runnable oldRunnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello, world!");
}
};
// 람다 표현식 사용
Runnable lambdaRunnable = () -> System.out.println("Hello, world!");
lambdaRunnable.run();
Stream API
컬렉션 데이터에 대한 복잡한 작업(필터링, 매핑, 축소 등)을 간결하게 처리할 수 있도록 도와준다. 반복(iteration)을 통해 작업을 처리하므로, 병렬 처리를 쉽게 할 수 있는 장점이 있다. (단점으론 병렬처리로 cpu를 많이 사용하게 된다.)
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 필터링과 매핑을 사용한 스트림 처리
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(filteredNames); // 출력: [ALICE]
interface default Method
기존 interface는 추상 메소드만을 정의할 수 있었는데 default 선언을 통해 method를 미리 정의해둘 수 있다. 또한 정의된 default 메소드는 상속된 class에서 오버라이딩 될 수 있다.
다음과 같이 default 메소드로 선언되면 상속받은 class에서 따로 구현하지 않아도 exec 메소드를 사용할 수 있으며 필요시 오버라이드 하여 사용할 수 있다.
public interface Calculator {
public int plus(int i, int j);
public int multiple(int i, int j);
default int exec(int i, int j){ //default로 선언함으로 메소드를 구현할 수 있다.
return i + j;
}
}
//Calculator인터페이스를 구현한 MyCalculator클래스
public class MyCalculator implements Calculator {
@Override
public int plus(int i, int j) {
return i + j;
}
@Override
public int multiple(int i, int j) {
return i * j;
}
}
public class MyCalculatorExam {
public static void main(String[] args){
Calculator cal = new MyCalculator();
int value = cal.exec(5, 10);
System.out.println(value);
}
}
Optional
Optional은 자바 8에서 도입된 클래스 중 하나로, null 값을 안전하게 처리하기 위해 사용한다. Optional을 사용하면 null을 직접 다루지 않아도 되며, 값의 존재 여부에 따라 안전하게 작업을 수행할 수 있다.
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
Optional<String> optionalValue = Optional.of("Hello, Optional!");
// 값이 존재하면 처리
optionalValue.ifPresent(System.out::println); // 출력: Hello, Optional!
// 값이 존재하지 않으면 대체 값 반환
String defaultValue = optionalValue.orElse("Default Value");
System.out.println(defaultValue); // 출력: Hello, Optional!
// 값이 존재하면 가져오고, 없으면 예외 발생
String value = optionalValue.orElseThrow(() -> new IllegalArgumentException("No value present"));
System.out.println(value); // 출력: Hello, Optional!
}
}
LocalDateTime
날짜 및 시간 API인 java.time 패키지는 LocalDateTime, LocalDate, LocalTime과 같은 클래스를 통해 더 직관적이고 사용하기 쉬운 날짜 및 시간 처리를 제공한다.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeExample {
public static void main(String[] args) {
// 현재 날짜와 시간 가져오기
LocalDateTime now = LocalDateTime.now();
System.out.println("Current DateTime: " + now);
// 특정 날짜와 시간 생성
LocalDateTime specificDateTime = LocalDateTime.of(2024, 9, 2, 10, 30);
System.out.println("Specific DateTime: " + specificDateTime);
// 날짜와 시간 포맷팅
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter);
System.out.println("Formatted DateTime: " + formattedDateTime);
// 날짜와 시간의 계산
LocalDateTime tomorrow = now.plusDays(1);
System.out.println("Tomorrow's DateTime: " + tomorrow);
}
}
참고 밸덩 모듈화 자료jigsaw
기존 java의 jar 패키징을 통해 모듈화를 진행하고 있지만, Jar Hell이 복잡한 ClassLoader로 정의시 JVM 컴파일은 성공하지만 런타임시 ClassNotFonundException을 마주하게 된다. 또한 Jar 자체가 무겁다. 때문에 gsaw라는 새로운 모듈화를 통해 가볍고 복잡하지 않은 java 모듈 시스템을 구축한 것이다.
이를 통해 애플리케이션을 더 작은 모듈로 나누어 관리할 수 있게 하며, 더 나은 접근 제어와 패키지 구조를 제공하고, 애플리케이션의 구조를 명확하게 하고, 모듈 간의 의존성을 명시적으로 관리할 수 있다.
// module-info.java
module com.example.myapp {
exports com.example.myapp.api;
requires java.base;
}
Stream API의 새로운 메서드 추가: takeWhile, dropWhile, iterate
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// takeWhile 사용 예시
List<Integer> taken = numbers.stream()
.takeWhile(n -> n < 5)
.toList();
System.out.println(taken); // 출력: [1, 2, 3, 4]
// dropWhile 사용 예시
List<Integer> dropped = numbers.stream()
.dropWhile(n -> n < 5)
.toList();
System.out.println(dropped); // 출력: [5, 6, 7, 8, 9, 10]
// iterate 사용 예시
Stream<Integer> iteratedStream = Stream.iterate(1, n -> n + 1)
.limit(10);
iteratedStream.forEach(System.out::println); // 1부터 10까지 출력
}
}
Optional의 ifPresentOrElse 메서드
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
Optional<String> optionalValue = Optional.of("Hello, JDK 9!");
optionalValue.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is absent")
);
Optional<String> emptyOptional = Optional.empty();
emptyOptional.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is absent")
); // 출력: Value is absent
}
}
Interface의 Private Method
interface에서 private method를 사용한다는 의미는 기존 interface에서 정의된 메서드는 상속받은 class에서 구현해야했다. 아니면 상속 받은 그대로 사용하던지 하지만 private method는 interface 내부에서만 사용하며 상속 받은 class는 해당 메서드를 따로 구현할 수 없는 것이다.
예를 들어 다음 코드를 봤을 때 method2()에서 method4()를 호출하여 사용하지만 실제 상속받은 CustomClass 에서 method4()를 오버라이딩할 수 없다.
public interface CustomInterface {
public abstract void method1();
public default void method2() {
method4(); //private method inside default method
method5(); //static method inside other non-static method
System.out.println("default method");
}
public static void method3() {
method5(); //static method inside other static method
System.out.println("static method");
}
private void method4(){
System.out.println("private method");
}
private static void method5(){
System.out.println("private static method");
}
}
public class CustomClass implements CustomInterface {
@Override
public void method1() {
System.out.println("abstract method");
}
public static void main(String[] args){
CustomInterface instance = new CustomClass();
instance.method1();
instance.method2();
CustomInterface.method3();
}
}
var 추가
Garbage Collector(GC) 병렬 처리 도입으로 인한 성능 향상
JVM heap 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능
var 키워드의 도입
var는 로컬 변수에만 사용할 수 있으며, 클래스 필드, 메소드 매개변수 또는 리턴 타입으로는 사용할 수 없다.
변수의 초기화 시점에 타입이 명확해야 하며, 추론된 타입은 변하지 않는다.
public class VarExample {
public static void main(String[] args) {
var message = "Hello, JDK 10!";
var number = 10;
var list = List.of("Apple", "Banana", "Cherry");
System.out.println(message); // 출력: Hello, JDK 10!
System.out.println(number); // 출력: 10
System.out.println(list); // 출력: [Apple, Banana, Cherry]
// 컴파일러가 message 변수를 String으로 추론
System.out.println(message.getClass().getSimpleName()); // 출력: String
}
}
Garbage Collector(GC) 병렬 처리 도입으로 인한 성능 향상
JVM heap 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능
JVM의 힙 메모리를 특수 메모리 영역에 할당할 수 있게 되면서, 대규모 데이터 처리나 고성능 컴퓨팅에서 더 나은 성능을 발휘할 수 있습니다. 이를 위해 JVM 옵션을 사용해 메모리 할당을 구성할 수 있습니다.
확장된 switch문
case문에 여러 경우를 넣을 수 있다.
// JDK 12 이전
String time;
switch (weekday) {
case MONDAY:
case FRIDAY:
time = "10:00-18:00";
break;
case TUESDAY:
case THURSDAY:
time = "10:00-14:00";
break;
default:
time = "휴일";
}
// JDK 12 이후
String time = switch (weekday) {
case MONDAY, FRIDAY -> "10:00-18:00";
case TUESDAY, THURSDAY -> "10:00-14:00";
default -> "휴일";
};
개선된 switch
변수를 선언하여 해당 변수에 switch문으로 값을 리턴 할 수 있다.
boolean result = switch (status) {
case SUBSCRIBER -> true;
case FREE_TRIAL -> false;
default -> throw new IllegalArgumentException(*"something is murky!"*);
};
개선된 multi line string
//Java 13 이전
String htmlBeforeJava13 = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
//Java 13 이후
String htmlWithJava13 = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
switch 표준화
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> {
String s = day.toString();
int result = s.length();
yield result;
}
};
//- default 코드 마지막라인에서 yield는 return을 의미함
instanceof 개선
이전에는 obj를 캐스팅 하는 것이 필요했지만 instanceof 연산자 뒤에 선언까지 가능해졌다.
// JDK 14 이전
if (obj instanceof String) {
String s = (String) obj;
}
java
// JDK 14 이후
if (obj instanceof String s) {
System.out.println(s.contains(*"hello"*));
}
record 선언
JDK 14 이전에는 다음과 같은 클래스 선언시 equals, hashcode, toString 이 잠재적으로 포함되어 있었다.
// Java 14 이전
final class Point {
public final int x;
public final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
//JDK 14 이후 record를 이용하여 간단하게 선언할 수 있게 되었다.
// Java 14 이후
record Point(int x, int y) { }
NullPointerException 개선
자바 개발자의 고질적인 예외인 NullPointerException이 개선되어, 어떤 변수가 Null인지를 설명해준다.
다음의 예외 메시지로 이전 버전과의 차이점을 알 수 있다.
author.age = 35;
---
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "age" because "author" is null
Sealed Classes
한글로 직역하면 봉인된 클래스로 Java에서 자주 사용하던 상속을 제한할 수 있다.
public sealed interface SafetyBelt permits Car, Truck {
String belt();
}
public final class Car implements SafetyBelt {
@Override
public String belt() {
// TODO Auto-generated method stub
return null;
}
}
public final class Truck implements SafetyBelt {
@Override
public String belt() {
// TODO Auto-generated method stub
return null;
}
}
위 코드와같이 SafetyBelt는 Car, Truck 에서 사용할 수 있도록 허용했고, 실제 상속받아서 belt 메소드를 구현한다. 참고로 상속받은 class는 추가 확장을 방지하기 위해 final로 선언한다.
public class Temp extends Car { // ERROR: Car의 상속이 막혀있으므로 추가 확장시 오류 발생
}
public interface Vehicle implements SafetyBelt { // 컴파일 ERROR 발생
}
이 sealed class 기능은 인터페이스 외에도 abstract class 에서도 사용할 수 있다.
// Pattern Matcing for Switch 적용된 코드
public String test(Object obj) {
return switch(obj) {
case Integer i -> "An integer";
case String s -> "A string";
case Cat c -> "A Cat"; // Cat 이라는 클래스를 선언했을 경우
default -> "I don't know what it is";
};
}
// JDK 17 이전 코드
public String test2(Object obj) {
if (obj instanceof Integer) {
return "An integer";
} else if (obj instanceof String) {
return "A string";
} else if (obj instanceof Cat) { // Cat 이라는 클래스를 선언했을 경우
return "A Cat";
} else {
return "I don't know what it is";
}
}