Open bsj805 opened 2 years ago
java 17 vs java 8 - the changes 실제로 모든 변화를 파악하고 싶다면 JEP를 보라. (JDK Enhancement Proposals) https://openjdk.java.net/jeps/0
API 변화를 찾으려면 https://javaalmanac.io/ 를 참고하라.
var
keyworld가 존재한다.// java 8 way
Map<String, List<MyDtoType>> myMap = new HashMap<String, List<MyDtoType>>();
List<MyDomainObjectWithLongName> myList = aDelegate.fetchDomainObjects();
// java 10 way
var myMap = new HashMap<String, List<MyDtoType>>();
var myList = aDelegate.fetchDomainObjects()
좀 더 간결하게 변수를 선언하는 방법을 제시한다. 다만, 타입을 숨기는것이 안좋은 때도 있을 것이다. 잘 가려서 사용해야 한다.
항상 불편했던 건 사실 람다
인데,
// causes compilation error:
// method reference needs an explicit target-type
var fun = MyObject::mySpecialFunction;
람다 함수를 담아 놓으려면 에러가 난다. method에 대한 reference는 target-type이 있어야 한다
고 한다.
boolean isThereAneedle = stringsList.stream()
.anyMatch((@NonNull var s) -> s.equals(“needle”));
람다 함수 내에서 var을 사용하는 것은 문제가 없다.
Records
어떤 이들은 롬복을 대체하기 위해 자바가 Records
를 만들었다고 한다.
Record는 데이터를 저장하기 위한 타입이다.
[…] a record acquires many standard members automatically:
- A private final field for each component of the state description; ==
모든 필드는 private final
- A public read accessor method for each component of the state description, with the same name and type as the -component; ==
getName() 이런식의 getter가 생기는 것이 아니라, name() 이런식으로 `필드명()` 형식의 `getter`가 생성된다
- A public constructor, whose signature is the same as the state description, which initializes each field from the - corresponding argument;
모든 필드를 초기화할 수 있는 생성자가 생성된다. record이름(필드1, 필드2, 필드3 ...) 과 같이
-Implementations of equals and hashCode that say two records are equal if they are of the same type and contain the same state; and같은 record인지 확인할 수 있는 hashCode랑 equals 함수를 만든다
- An implementation of toString that includes the string representation of all the record components, with their names. ''' 모든 record 필드가 가지는 값의 string representation과 그 필드 이름을 가진다
`` 읽어보면,
enum과 비슷하고, LOMBOK의
@value` 와도 비슷하다고 한다. record 설명
public class SampleRecord {
private final String name;
private final Integer age;
private final Address address;
public SampleRecord(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public Address getAddress() {
return address;
}
}
이런 코드가 있으면 우리는 record로 저장하는 것이 더 편하다.
public record SampleRecord(
String name,
Integer age,
Address address
) {}
근데 이렇게 해두면 @ResponseBody
같이 JSON으로 serialize가 안된다.
public record SampleRecord(
@JsonProperty("name") String name,
@JsonProperty("age") Integer age,
@JsonProperty("address") Address address
) {}
record는 static
변수, static
, public
메서드를 가질 수 있다.
Enum과 다른 부분은
record BankAccount (String bankName, String accountNumber) implements HasAccountNumber {
public BankAccount { // <-- this is the constructor! no () !
if (accountNumber == null || accountNumber.length() != 26) {
throw new ValidationException(“Account number invalid”);
}
// no assignment necessary here!
}
}
그냥 알아서 값이 들어간다고 한다.
그래서
public record UserRecord(
String name,
Integer age,
EmailRecord email
) {
public String getInfo() {
return this.email().id();
}
}
### EmailRecord.java
public record EmailRecord(
String id,
String corporate
) {}
이런식으로 record안의 record를 만들어두고,
@PostMapping("/helloPost")
public String getRecordPost(UserRecord userRecord) {
System.out.println(userRecord.getInfo());
return "index";
}
이런식으로 form을 받을 수 있게 하고,
<input>
을
이런식으로 해서 submit을 하게 했더니,
java.lang.NullPointerException: Cannot invoke "com.example.java17practice.records.EmailRecord.id()" because the return value of "com.example.java17practice.records.UserRecord.email()" is null] with root cause
즉, email이라는 record 객체가 비어있어서 어쩔수가 없다.
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
이번엔 또 이런 에러
알고보니, @RequestBody
annotation같은 것들은 json 형식의 content-type application/json
과 같이 받아야 한다.
그래서 나처럼 email record를 설정해둬도 채울수도 있는거고.
그래서 post 요청을 보내는 툴을 써보면
{"name":"qwe1","age":"15","id":"1qwe","corporate":"naver"}
에 대응해서
UserRecord[name=qwe1, age=15, id=1qwe, corporate=naver]
이런식으로 채워져있는 것을 발견할 수 있다.
또는,
{"name":"qwe1","age":"15", "email":{"id":"1qwe","corporate":"naver"}}
에 대응해서
UserRecord[name=qwe1, age=15, email=EmailRecord[id=1qwe, corporate=naver]]
간단하게 커맨드객체를 채울 수 있게 된다.
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
case SATURDAY, SUNDAY -> true;
};
이런식으로, case문에서 return을 시킬 수 있다.
@GetMapping("/apis/{id}")
public String findId(@PathVariable String id) {
String name = switch (id) {
case "a", "b", "c" -> {
System.out.println("hello" + id);
yield "case" + id;
}
case "sd" -> "sd";
default -> "default" + id;
};
name += " manm";
return name;
}
block statement도 쓸 수 있다. 다만, 블록 안에서 return할 때에는 yield 키워드를 사용한다. switch에 대한 결과물을 yield로 정할 수 있다.
Instanceof
사용시 편의 추가if (obj instanceof MyObject) {
MyObject myObject = (MyObject) obj;
// … further logic
}
이제 local variable을 if문 안에서 생성할 수 있게 되면서
if (obj instanceof MyObject myObject) {
// … the same logic
}
이런식으로 사용할 수 있게 된다. myObject = obj 를 해주는 구조다.
if (obj instanceof MyObject myObject && myObject.isValid()) {
// … the same logic
}
이런식으로 같은 줄에서 먼저 실행되기만 하면 생성된 지역변수를 사용할 수 있다.
switch문에서 받을 수 있는 모든 value에 대해 case 처리를 했는데도 default 안 썼다고 워닝이 난다.
did the "no default" warning in switch ever annoy you?
Sealed Class
는 instanceof 타입 체킹에 대한 워닝을 없애준다.
public abstract sealed class Animal permits Dog, Cat {
}
public final class Dog extends Animal {
}
public final class Cat extends Animal {
}
이런 코드를 구성하면, #4, #5 를 합쳐서
if (animal instanceof Dog d) {
return d.woof();
}
else if (animal instanceof Cat c) {
return c.meow();
}
사실 java단에서 부모 클래스는 어떤 자식 클래스들이 있는지 알지 못한다. 따라서 상속받는 클래스들을 Dog
, Cat
으로 제한 하면서
public String getAnimalName(Animal animal) {
if (animal instanceof Cat cat) {
return "cat";
}
return "dog";
}
이런 로직이 가능하다. 즉 Animal
클래스는 무조건 cat이나 dog를 준다는 것이 보장되어 있다는 것.
String myWallOfText = ”””
______ _ _
| ___ \ | | (_)
| |_/ / __ ___| |_ _ _ _ ___
| __/ '__/ _ \ __| | | | / __|
| | | | | __/ |_| | |_| \__ \
\_| |_| \___|\__|_|\__,_|___/
”””
원래는 이게 한줄마다 \
와 같이 escaping quotes 나 newline이 들어갔어야 했는데, """
으로 해결하게 되었다.
company.getOwner().getAddress().getCity();
이런식의 코드를 짜게 되었을 때, NPE가 났을 때 어떤 오브젝트가 NULL인지 찾기 어려웠는데, 이 경우에도 JVM이 "cannot invoke Person.getAddress()"
이런식으로 문제를 정확히 보여준다.
fully non-asynchronous 한 HttpClient를 이용하는 것이 가능하다.
Optional.orElseThrow()
메서드Optional
의 get()
메서드는 Optional
로 정의되어 있는 value를 얻기 위해 사용되었다.
만약 그래서 값이 저장 안되어 있다면 Exception을 throw하게 된다.
MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.get();
이 경우에는 Exception Handling이 필요하다. 그런데 일반적으로 코드만 봤을 때 throw
하는 코드인지 알기 어렵다
아래 코드는 위 코드랑 똑같지만, object가 발견 안되면 throw한다는 것을 알 것이다.
MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.orElseThrow();
1. JAVA 17이 무엇인가
java 8버전, 11버전 이후 2021년 9월 출시된 LTS 버전.
2. 실제로 자바 8 에서 17로 바뀐 것은?
velog- java17로의 전환을 고려하는 이유
3. java 8 에서 java 17로 변경해야 하는가?
장점