String 클래스에 새로운 메소드 추가
indent()
String text = "Hello Baeldung!\\nThis is Java 12 article.";
text = text.indent(4);
System.out.println(text);
text = text.indent(-10);
System.out.println(text);
// Hello Baeldung!
// This is Java 12 article.
//
//Hello Baeldung!
//This is Java 12 article.
transform()
@Test
public void givenString_thenRevertValue() {
String text = "Baeldung";
String transformed = text.transform(value ->
new StringBuilder(value).reverse().toString()
);
assertEquals("gnudleaB", transformed);
}
File::mismatch 함수
public static long mismatch(Path path, Path path2) throws IOException
두 파일을 비교해서 첫번째로 미스매치되는 위치를 찾는 함수이다.
반환값은 0L부터 시작하여 더 작은 크기를 갖는 파일의 byte 사이즈까지이며 동일한 파일의 경우 -1L을 반환한다.
아래 코드는 동일한 파일을 비교하는 예시이다. -1L을 반환한다.
@Test
public void givenIdenticalFiles_thenShouldNotFindMismatch() {
Path filePath1 = Files.createTempFile("file1", ".txt");
Path filePath2 = Files.createTempFile("file2", ".txt");
Files.writeString(filePath1, "Java 12 Article");
Files.writeString(filePath2, "Java 12 Article");
long mismatch = Files.mismatch(filePath1, filePath2);
assertEquals(-1, mismatch);
}
아래 코드는 다른 파일을 비교하는 예시이다. 8L을 반환한다.
@Test
public void givenDifferentFiles_thenShouldFindMismatch() {
Path filePath3 = Files.createTempFile("file3", ".txt");
Path filePath4 = Files.createTempFile("file4", ".txt");
Files.writeString(filePath3, "Java 12 Article");
Files.writeString(filePath4, "Java 12 Tutorial");
long mismatch = Files.mismatch(filePath3, filePath4);
assertEquals(8, mismatch);
}
Teeing Collector
2개의 컬렉터와 1개의 함수를 인자로 받아 각 컬렉터의 기능을 실행하고 그 컬렉터의 결괏값을 기반으로 함수를 실항하는 복합 함수
아래 코드는 sum 컬렉터와 count 컬렉터의 반환값을 토대로 평균을 구하는 teeing 함수 예제다.
@Test
public void givenSetOfNumbers_thenCalculateAverage() {
double mean = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.teeing(Collectors.summingDouble(i -> i),
Collectors.counting(), (sum, count) -> sum / count));
assertEquals(3.0, mean);
}
Compact Number Formatting
public static NumberFormat getCompactNumberInstance(Locale locale, NumberFormat.Style formatStyle)
아래 예제는 2592를 미국식으로 short은 2.59K, long은 2.59 thousand로 표현하는 코드이다.
@Test
public void givenNumber_thenCompactValues() {
NumberFormat likesShort =
NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.SHORT);
likesShort.setMaximumFractionDigits(2);
assertEquals("2.59K", likesShort.format(2592));
NumberFormat likesLong =
NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.LONG);
likesLong.setMaximumFractionDigits(2);
assertEquals("2.59 thousand", likesLong.format(2592));
}
확장 switch 식(expression)
break; 키워드도 생략되는 문법yield 키워드를 통해 → 를 통한 단순 반환 말고 코드 블럭을 생성하여 복잡한 기능을 구현하고 값을 반환해줄 수도 있다.DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
case SATURDAY, SUNDAY -> true;
};
public class SwitchExpression {
public static void main(String[] args) {
int days = 0;
Month month = Month.APRIL;
days = switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31;
case FEBRUARY -> 28;
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
default -> throw new IllegalStateException();
};
}
}
//yield
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
System.out.println("Work work work");
yield false;
}
case SATURDAY, SUNDAY -> {
System.out.println("Yey, a free day!");
yield true;
}
//코드 블록 외에서 사용하면 에러가 발생하니 주의
default -> yield true;
};
public class SwitchExpression {
public static void main(String[] args) {
int days = 0;
Month month = Month.APRIL;
days = switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> {
System.out.println(month);
yield 31;
}
case FEBRUARY -> {
System.out.println(month);
yield 28;
}
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> {
System.out.println(month);
yield 30;
}
default -> throw new IllegalStateException();
};
}
}
더 나아진 NullPointerExceptions
//기존엔 이 한줄은 어디서 에러가 났는지 디버거모드로 찍어보지 않으면 몰랐다.
//이젠 “cannot invoke Person.getAddress()”. 라고 명확히 알려준다.
company.getOwner().getAddress().getCity();
int[] arr = null;
arr[0] = 1;
//Exception in thread "main" java.lang.NullPointerException
//at com.baeldung.MyClass.main(MyClass.java:27)
//이렇게 변했다.
//java.lang.NullPointerException: Cannot store to int array because "a" is null
Records
불변 데이터 객체를 생성하는 새로운 유형의 클래스
기존방식
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
레코드 방식
//시그니처만 구현해줘도 되고, 위 코드보다 짧고 간결하면서 생성자에 조건까지 추가할 수 있다.
public record Person(String name, int age) {
public Person {
if(age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
}
TextBlocks
텍스트블럭 선언 가능
String myWallOfText = """
______ _ _
| ___ \\ | | (_)
| |_/ / __ ___| |_ _ _ _ ___
| __/ '__/ _ \\ __| | | | / __|
| | | | | __/ |_| | |_| \\__ \\
\\_| |_| \\___|\\__|_|\\__,_|___/
"""
String myPoem = """
Roses are red, violets are blue - \\
Pretius makes the best software, that is always true
"""
//아래와 동일한 결과
String myPoem = "Roses are red, violets are blue - Pretius makes the best software, that still is true."
Sealed Class
final, non-sealed, sealed 중 하나로 선언되어야 한다.non-sealed 키워드를 선언하면 허용되지 않은 클래스여도 똑같이 상속 구현할 수 있다. 이 키워드는 의도와 기능을 명확히 한 뒤에만 사용해야 한다.public abstract sealed class Person
permits Employee, Manager {
//...
}
public final class Employee extends Person {
}
public non-sealed class Manager extends Person {
}
Proxy 인스턴스에서인터페이스의 default 메소드를 리플렉션을 통해 실행할 수 있다.
Day Period Support
LocalTime date = LocalTime.parse("15:25:08.690791");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h B");
assertThat(date.format(formatter)).isEqualTo("3 in the afternoon");
Records (14 프리뷰, 15 정식이였던 기능을 개선한 버전)
기존방식 추가해야할 것
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
레코드방식
public record Person(String name, int age) {
//나이 제한하기 등 동작을 재정의할 수 있음, 생성자이기 때문에 () 사용하지 말것
public Person{
if(age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
}
public class RecordDemo {
public static void main(String[] args){
Person person = new Person("Ted", 100);
System.out.println("이름:"+ person.name() + " 나이:"+person.age());
System.out.println("객체 정보:"+person.toString());
Person person2 = new Person("Ted", 100);
Person person3 = new Person("Dean", 200);
if (person.equals(person2)) System.out.println("person, person2는 같은 사람");
else System.out.println("person, person2는 다른 사람");
if (person.equals(person3)) System.out.println("person, person3는 같은 사람");
else System.out.println("person, person3는 다른 사람");
}
}
이너클래스에서도 사용할 수 잇다.
class OuterClass {
class InnerClass {
Book book = new Book("Title", "author", "isbn");
}
}
instanceof 패턴 매칭
if 문에 할당한 변수를 사용할 수도 있다.if (obj instanceof MyObject) {
MyObject myObject = (MyObject) obj;
// … further logic
}
if (obj instanceof MyObject myObject) {
// … the same logic
}
if (obj instanceof MyObject myObject && myObject.isValid()) {
// … the same logic
}
//또는
public class PatternMatching {
public static double price(Vehicle v) {
if (v instanceof Car c) {
return 10000 - c.kilomenters * 0.01 -
(Calendar.getInstance().get(Calendar.YEAR) -
c.year) * 100;
} else if (v instanceof Bicycle b) {
return 1000 + b.wheelSize * 10;
} else throw new IllegalArgumentException();
}
}