Open bsj805 opened 2 years ago
lambda 표현식을 더 간단하게 표현하는 방법으로,
Consumer<String> func = text -> System.out.println(text);
func.accept("Hello");
// 실행 결과
// Hello
이런식으로 표현해야 했던 것을,
Consumer<String> func = System.out::println;
func.accept("Hello");
// 실행 결과
// Hello
``` 이렇게 표현할 수 있게 된 것.
인자가 두개 이상인 경우
https://stackoverflow.com/questions/23139725/using-method-reference-instead-of-multi-argument-lambda
static 메서드냐, instance 메서드냐에 따라서
```java
(a, b, c) -> SomeClass.func(a, b, c); //static 메서드의 경우 SomeClass::func와 동일
(a, b, c) -> a.func(b, c); // instance 메서드의 경우 SomeClass::func와 동일
추상클래스나 인터페이스에 디폴트 메서드를 넣어서 필요한 메서드만 오버라이딩 할 수 있도록 사용가능
1) null을 체크하는 모듈을 만들어요. 모듈 이름을 "NonNull"이라고 할게요
2) 다음과 같이 사용하면되요
@NonNull String str; //
https://www.javatpoint.com/java-8-type-annotations-and-repeating-annotations
이 경우 str이 null이면 throw
해버린다.
empty arrayList 못 만들게 하려면
public class TypeAnnotations {
public static void main(String[] args) {
List<String> request =
new @NotEmpty ArrayList<>(Arrays.stream(args).collect(
Collectors.toList()));
}
}
이런식으로 해야 한다. Emtpy이면 throw하게 될 것이다.
public class TypeAnnotations {
public static void main(String[] args) {
List<@Email String> emails;
}
}
이런식으로 이메일 포맷에 맞는 String만 있다는 것을 보장할 수 있다.
Cron
식처럼
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
여러번 annotation을 사용하고 싶을 수 있다. 아래는, annotation으로 된 값을 가져와서 단순히 출력해보는 예제
// Importing required packages for repeating annotation
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Declaring repeatable annotation type
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
// Declaring container for repeatable annotation type
@Retention(RetentionPolicy.RUNTIME)
@interfaceGames{
Game[] value();
}
// Repeating annotation
@Game(name = "Cricket", day = "Sunday")
@Game(name = "Hockey", day = "Friday")
@Game(name = "Football", day = "Saturday")
public class RepeatingAnnotationsExample {
public static void main(String[] args) {
// Getting annotation by type into an array
Game[] game = RepeatingAnnotationsExample.class.getAnnotationsByType(Game.class);
for (Gamegame2 : game) { // Iterating values
System.out.println(game2.name()+" on "+game2.day());
}
}
}
파라미터의 이름을 runtime에 얻는 것을 허용하는 문법이다. https://stackoverflow.com/questions/2237803/can-i-obtain-method-parameter-name-using-java-reflection If I have a class like this:
public class Whatever
{
public void aMethod(int aParam);
}
is there any way to know that aMethod uses a parameter named aParam, that is of type int?
즉 파라미터로 aParam
을 쓰는데, 이 이름을 얻을 수 있다.
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
파라미터 이름들을 가져올 수 있다.
autoclosable resource on a try-catch
block.
JVM보고 이 코드가 실행 된 이후에 이 resource들을 해제 시켜도 된다 라고 말할 수 있다.
단, declared resource가 Autoclosable
interface를 implement
했어야 한다.
이를테면, 수동으로 자원을 해제하려면 예전에는
public class TryWithResources {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(
new StringReader("Hello world example!"));
try {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
이런식으로 finally를 이용해야 했다.
하지만, java8부터는 try 를 선언하면서 resource
를 선언할 수 있다.
public class TryWithResources {
public static void main(String[] args) {
final BufferedReader br3 = new BufferedReader(
new StringReader("Hello world example3!"));
try (BufferedReader reader = br3) {
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Error happened!");
}
}
}
reader가 매번 해제될 것을 알 수 있다.
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
public class DiamondOperator {
StringAppender<String> appending = new StringAppender<>() {
@Override
public String append(String a, String b) {
return new StringBuilder(a).append("-").append(b).toString();
}
};
public abstract static class StringAppender<T> {
public abstract T append(String a, String b);
}
}
단순히 String a와 b가 주어지면, a - b
를 만드는 append라는 함수가 있다.
지금 append라는 메서드를 쓰려고 익명클래스를 사용한 경우인데, StringAppender<>
이 부분에 diamond operator가 있으면 java8
까지는 컴파일 에러가 났다. 하지만 이제 유추할 수 있기 때문에 java9
부터는 오류가 나지 않는다.
java8
에서는 인터페이스에서 private methods를 사용할 수 없다.
보통 복잡한 구현 로직에 필요한 함수들은 private으로 감추는 경우가 많은데, java 9
에서는 interface에서도 private method를 사용할 수 있게 한다.
public class PrivateInterfaceMethods {
public static void main(String[] args) {
TestingNames names = new TestingNames();
System.out.println(names.fetchInitialData());
}
public static class TestingNames implements NamesInterface {
public TestingNames() {
}
}
public interface NamesInterface {
default List<String> fetchInitialData() {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(this.getClass()
.getResourceAsStream("/names.txt")))) {
return readNames(br);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private List<String> readNames(BufferedReader br)
throws IOException {
ArrayList<String> names = new ArrayList<>();
String name;
while ((name = br.readLine()) != null) {
names.add(name);
}
return names;
}
}
}
이런식으로, 원래는 readNames(BufferedReader br)
를 다른 utils 클래스 같은데에서 static 메서드로 두어서 처리를 하거나,
위의 fetchInitialData()
함수가 엄청 길어지는 코드를 짰어야 했을 것이다.
자바에서 항상 지역변수를 선언할 때 explicit type이 필요 했는데, 사실 우리가 쓰고 읽을 때 어떤 type이 필요한지 그냥 뻔하다.
따라서 많은 부분의 코드는 쓸모없는 type선언으로 채워져 있다.
var
타입은 우리가 type을 신경쓰지 않도록 해준다.
public class LocalTypeVar {
public void explicitTypes() {
Person Roland = new Person("Roland", "Deschain");
Person Susan = new Person("Susan", "Delgado");
Person Eddie = new Person("Eddie", "Dean");
Person Detta = new Person("Detta", "Walker");
Person Jake = new Person("Jake", "Chambers");
List<Person> persons =
List.of(Roland, Susan, Eddie, Detta, Jake);
for (Person person : persons) {
System.out.println(person.name + " - " + person.lastname);
}
}
}
이 옛날 버전의 코드는 그냥 Person
list를 만들어서 print하는 코드이다.
var
을 이용하면 type 선언이 따로 필요없다.
public class LocalTypeVar {
public void varTypes() {
var Roland = new Person("Roland", "Deschain");
var Susan = new Person("Susan", "Delgado");
var Eddie = new Person("Eddie", "Dean");
var Detta = new Person("Detta", "Walker");
var Jake = new Person("Jake", "Chambers");
var persons = List.of(Roland, Susan, Eddie, Detta, Jake);
for (var person : persons) {
System.out.println(person.name + " - " + person.lastname);
}
}
}
var
키워드를 이제 lambda expressions
에서 사용할 수 있다.
a
가 이름에 들어 있지 않은 이름들을 필터링하는 로직을 다시 짜보면,
public class LocalTypeVarLambda {
public void explicitTypes() {
var Roland = new Person("Roland", "Deschain");
var Susan = new Person("Susan", "Delgado");
var Eddie = new Person("Eddie", "Dean");
var Detta = new Person("Detta", "Walker");
var Jake = new Person("Jake", "Chambers");
var filteredPersons =
List.of(Roland, Susan, Eddie, Detta, Jake)
.stream()
.filter((var x) -> x.name.contains("a"))
.collect(Collectors.toList());
System.out.println(filteredPersons);
}
}
사실 근데 var 키워드 빼고 (x) -> x.name.contains("a")
를 해도 var
이 생략된 것이다. 내부 로직은 똑같이 동작한다.
월 별로 날짜를 리턴하는 코드를 switch문을 짠다고 했을 때,
public class SwitchExpression {
public static void main(String[] args) {
int days = 0;
Month month = Month.APRIL;
switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER :
days = 31;
break;
case FEBRUARY :
days = 28;
break;
case APRIL, JUNE, SEPTEMBER, NOVEMBER :
days = 30;
break;
default:
throw new IllegalStateException();
}
}
}
근데 이제 break
없이도 리턴시켜서 바로 스위치문을 탈출하는 로직을 만들 수 있다
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();
};
}
}
두개를 섞어 쓸 수는 없다.
String name = switch (id) {
case "a", "b", "c" -> {
System.out.println("hello" + id);
yield "case" + id;
}
case "szz":
case "sd" -> "sd";
default -> "default" + id;
};
이런식으로 :
을 쓰려고 하면 Different case kinds used in the switch
라고 에러가 뜨면서 실패한다.
yield
키워드는 switch안에서의 리턴값을 만들어준다. (return
을 내부에서 쓸 수는 없다.) block
에서의 return값을 yield
로 처리한다고 한다.
public class TextBlocks {
public static void main(String[] args) {
System.out.println(
"""
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p>This is an example of a simple HTML
page with one paragraph.</p>
</body>
</html>
"""
);
}
}
이런식으로, text를 뭉텅이로 쓸 수 있게 바뀌었다.
"""
라는 문법이 있다. 단, 이걸 쓴 다음에 무조건 newline이 들어가줘야 제대로 인식한다.
Illegal text block start: missing new line after opening quotes.
에러가 날 수 있다.
inline
으로 variable을 선언해서 if
-else
블럭 안에서 사용할 수 있다.
만약 instanceof
가 맞으면 해당 variable에 assign이 된다.
public static double priceOld(Vehicle v) {
if (v instanceof Car) {
Car c = (Car) v;
}
이런식으로 썼다면
public static double priceOld(Vehicle v) {
if (v instanceof Car c) {
// c를 사용할 수 있다
}
단, 여기서의 local variable c
의 scope는 저 if 블록 안으로 한정된다.
default로 final인 enum같은 클래스로,
public record VehicleRecord(
String code,
String engineType
) {}
이 한줄만 써도
등이 생성되어 간편하게 쓸 수 있다.
final
을 class에 붙이면 다른 클래스들이 extend
할 수 없다.
일부 class
들만 extend
할 수 있게 허용하려면 어떻게 해야 할까.
sealed class
가 있다.
public sealed class Vehicle permits Bicycle, Car {...}
이렇게 하면 Vehicle
은 무조건 Bicycle
과 Car
중 하나라는 보장이 있다.
하위 클래스인 Bicycle
도
public final class Bicycle extends Vehicle {...}
이런식으로 final을 붙여놔야 한다.
https://reflectoring.io/java-release-notes/