Skip to content

Commit

Permalink
refactor: #29 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
Livenow14 committed Sep 18, 2021
1 parent c6735cc commit 4654fb3
Showing 1 changed file with 260 additions and 9 deletions.
269 changes: 260 additions & 9 deletions java/q5-Java7과 Java8의 특징애 대해 설명해주세요/gump.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 정리

언젠가는 한번 정리해봐야지 했는데, 정리 하다보니 역시 Java8이 오랬동안 사랑받는 이유를 알거 같아요(추가, 변경사항이 엄청나군요..!). 가장 큰 특징들을 그나마 추려봤어요.

## Java7의 특징

### Try-with-resources 지원
Expand Down Expand Up @@ -174,6 +176,8 @@ public class Machine<T> {

애노테이션을 통해 컴파일 예외가 발생하지 않음

<br>

## Java8의 특징

### Heap Permanent Generation 제거
Expand Down Expand Up @@ -213,33 +217,274 @@ String producer = Vehicle.producer();

### Functional interfaces

함수형 인터페이스
하나의 기능을 제공하는 단 하나의 추상메서드를 정의하는 인터페이스가 추가됐어요.
기능을 위해 default메소드는 원하는 만큼 추가할 수 있어요.
@FunctionalInterface와 함께 쓰이며, 부적절하게 사용될 때 컴파일 하지 않게 할 수 있어요.

`Comsumer`

```java
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);

/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
```

### Lambda expressions

람다 표현식
람다식은 메서드로 전달할 수 있는 익명 함수를 단순화 한 것이에요.
쉽게 객체 대신 코드를 메서드의 파라미터로 전달하거나, 데이터 그룹을 처리하여 알고리즘을 실행하는데 사용돼요.

**내부/외부 Iteration**

외부반복

```java
for (String value: myCollection) {
System.out.println(value);
}
```

내부반복

```java
myCollection.forEach(value -> System.out.println(value));
```

**passing code with behavior parameterization** (동적 파라미터화 코드 전달)

`Before`

```java
public class Apple {
private final int weight;

public Apple(int weight) {
this.weight = weight;
}

public int getWeight() {
return weight;
}
}
```

```java
void before() {
List<Apple> apples = Arrays.asList(new Apple(10),
new Apple(30),
new Apple(50),
new Apple(70));
List<Apple> inventory = new ArrayList<>();

for (Apple apple : apples) {
if (apple.getWeight() > 50) { //동작
inventory.add(apple);
}
}
for (Apple apple : inventory) {
System.out.println("apple.getWeight() = " + apple.getWeight());
}
}
```

위에서의 동작은 조건문 부분이라 볼 수 있어요. 이부분을 동작 파라미터를 사용해서 리팩토링해보면

`After`

```java
void after() {
List<Apple> apples = Arrays.asList(new Apple(10),
new Apple(30),
new Apple(50),
new Apple(70));
List<Apple> inventory = filterApples(apples, it -> it.getWeight() > 50 );

for (Apple apple : inventory) {
System.out.println("apple.getWeight() = " + apple.getWeight());
}
}

public static List<Apple> filterApples(List<Apple> apples, Predicate<Apple> p) {
List<Apple> inventory = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
inventory.add(apple);
}
}
return inventory;
}
```

같은 결과를 출력하는 것을 알 수 있다.

### Method References

메서드 참조
메서드 참조는 람다식을 좀더 짧고 읽기 쉽게 사용하는 것이에요. 메서드 참조에는 4가지 방법이 있어요.

**정적 메서드 참조**

정적 메서드에 대한 참조는 `클래스::메소드명`으로 사용돼요. 먼저 람다식을 사용한 경우를 살펴보면 아래와 같아요.

```java
boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));
```

이를 메서드 참조로 변경하면

```java
boolean isReal = list.stream().anyMatch(User::isRealUser);
```

위와 같이 사용돼요.

**인스턴스 메서드 참조**

인스턴스 메서드에 대한 참조는 `인스턴스::메소드명`으로 사용돼요.
아래 코드는 User의 isLegalName(String string)을 호출해요.

```java
User user = new User();
boolean isLegalName = list.stream().anyMatch(user::isLegalName);
```

**특정 유형의 객체의 인스턴스 메서드 참조**

특정유형(String, BigDecimal 등)

```java
long count = list.stream().filter(String::isEmpty).count();
```

**생성자 참조**

생성자 참조는 `클래스명::new`로 사용해요. 생성자는 특수한 메소드이기 대문에, 메소드 이름으로 new를 사용해요.

```java
Stream<User> stream = list.stream().map(User::new);
```

### Date and time API 지원

날짜와 시간 API 제공
이전에는 날짜와 시간을 처리하는 것이 굉장히 귀찮았어요. 월요일이 0 또는 1으로 시작하는지, moth는 0으로 시작하는 지 등을 파악 했어야해요.
이를 편리하게 해주는 API가 추가됐어요.

`javax.time.Clock`

```
Clock.systemUTC(); //current time of your system in UTC.
Clock.millis();//time in milliseconds from 1/1/1970.
```

`javax.tme.ZoneId`

```
ZoneId zone = ZoneId.of(“Europe/London”);//zoneId from a timezone.
Clock clock = Clock.system(zone);//set the zone of a Clock.
```

`javax.time.LocalDate`

```
LocalDate date = LocalDate.now();//current date
String day = date.getDayOfMonth();//day of the month
String month = date.getMonthValue();//month
String year = date.getYear();//year
```

### Optional<T> 지원

Optional을 제공하여 null에 대한 참조를 안전하게 해줌
Optional을 제공하여 null에 대한 참조를 안전하게 할 수 있게 됐어요.
이전에는 어떠한 객체를 참조할 때 null인지 아닌지 검사를 무조건 했어요(코드량이 많아지면 엄청 귀찮았겟죠?).
이러한 불편함을 해소하기 위해, NPE를 얻을 상황을 처리하는 Optinal이 추가가 됐어요.
Optinal <T>에서, T의 값이 null이면, 빈 Optinal를 반환하기에, 비어있을 경우 정의된 작업을 처리할 수 있게 됐어요.
`befer`

```java
User user = getUser();
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String street = address.getStreet();
if (street != null) {
return street;
}
}
}
return "not specified";
```

`after`

### Collectors
```java
Optional<User> user = Optional.ofNullable(getUser());
String result = user
.map(User::getAddress)
.map(Address::getStreet)
.orElse("not specified");
```

아래와 같이 Optional에서 제공하는 메서드 들을 통해, null참조 가능성이 있는 상황에 유용하게 쓰일 수 있게 됐어요. (더 많은 메소드는 java.util.Optional확인)

### forEach
**Optional.empty()**

```java
Optional<String> optional = Optional.empty();
```

**Optional.ofNullable(T t) && Optional.of(T t)**

```java
Optional<String> optional = Optional.ofNullable(getString());
```

### Stream API

### Stream Filter
Stream이란 순차/병렬 작업을 지원하는 어떠한 순차적인 요소예요. (java.util.stream.Stream)
쉽게 스트림을 데이터 컬렉션 반복을 멋지게 처리하는 기능이라 생각하면 편해요.

간단히 특징에 대해 정리하면

### StringJoiner
- 스트림 API의 특징에는 선언형, 조립할 수 있음, 병렬화가 있다.
- 스트림은 연속된 요소, 중간연산, 최종연산으로 이루어진다.
- 자바 컬렉션은 외부반복, 스트림은 내부반복을 사용한다.
- 중간 연산은 파이프라인으로 구성되어 최종 연 산에서 한 번에 처리된다. 이를 지연 계산된다고 한다.
- 중간 연산은 stream을 반환하고, 최종 연산은 스트림이 아닌 결과을 반환한다.
- 중간 연산의 예시로는 map, filter, flatMap 가 있고, 최종 연산의 예시로는 count, foreach, collect가 있다.

**데이터 컬렉션(컬렉션)과 스트림의 차이는?**

문서에도 잘 나와있지만, 둘의 차이는. 크게 목표하는 바가 다르다는 거에요.
컬렉션은 데이터를 어떻게 저장/관리하고 접근하는지를 목표하고 있어요.
이와 반대로 스트림은 데이터를 직접 접근하거나 조작하는 기능을 제공하지 않으며, 데이터를 어떻게 계산할지에 대한 목표를 가지고있어요.
즉, 데이터의 저장/관리가 목적이라면 컬렉션을, 데이터의 계산이 목적이라면 스트림을 사용하는게 좋다고 볼 수 있어요.

### Collections

Stream APICollections API가 통합되어 Iterator를 상속하는 컬랙션과 Map 컬렉션에 대한 대량 작업이 가능하게 됐어요.

### Parallel Array Sorting (배열 정렬의 병렬처리)

Expand All @@ -264,6 +509,12 @@ byte[] asBytes = Base64.getDecoder().decode("your base 64 string");

<br>

## Refer

- [http://openjdk.java.net/projects/jdk8/features#150](http://openjdk.java.net/projects/jdk8/features#150)
- [https://manifesto.co.uk/java-jdk-8-features/](https://manifesto.co.uk/java-jdk-8-features/)
- [https://www.baeldung.com/java-8-new-features](https://www.baeldung.com/java-8-new-features)

### 질문

varargs란?
Expand Down

0 comments on commit 4654fb3

Please sign in to comment.