Java 8에 도입된 기능적 스타일은 언어에 큰 도움이 됩니다. 이제 Java는 순수한 OOP가 아니며 OOP와 함수형 프로그래밍의 하이브리드입니다. 이것은 게임 체인저이며 이러한 변화를 흡수하려면 OOP 두뇌를 바꿔야 합니다. 그런데 왜 우리는 이러한 변화를 받아들여야 할까요? 순수 OOP를 사용하여 문제를 해결할 수 있는데 왜 함수형 스타일과 어울리려고 시간을 낭비해야 합니까?
-
Java 8에 도입된 기능적 스타일은 비즈니스 로직과 코드 간의 격차를 줄이는 데 도움이 됩니다. 이를 통해 더 높은 수준에서 자연스러운 흐름으로 이야기를 전달할 수 있습니다. 어떻게 하고 싶은지 말하는 대신, 무엇을 하고 싶은지 말할 수 있습니다.
-
코드가 더 깔끔하고 간결해졌습니다.
-
고차 함수를 사용하면 다음을 수행할 수 있습니다.
- 함수를 다른 함수로 보내기
- 다른 함수 안에 함수 만들기
- 다른 함수의 함수 반환
이는 이를 수행하기 위해 객체를 보내고, 생성하고, 반환해야 하는 Java의 큰 승리입니다. 우리는 더욱 안정적이고 집중적이며 재사용하기 쉬운 코드를 작성할 수 있을 것입니다.
-
람다 덕분에 우리는 게으른 계산을 할 수 있습니다. 람다 식이 메서드 인수로 전송되면 컴파일러는 메서드에서 호출될 때 이를 평가합니다. 이는 즉시 평가되는 일반 메서드 인수와 다릅니다.
-
람다는 단위 테스트 작성을 재미있게 만듭니다. 이를 통해 깨끗하고 크기가 작으며 작성이 빠른 경량 테스트를 만들 수 있습니다. 람다를 사용하여 테스트 중인 코드를 근절할 수 있습니다. 이를 통해 모든 종류의 시나리오가 코드에 어떤 영향을 미치는지 테스트할 수 있습니다.
-
배울 수 있는 새로운 패턴.
-
그리고 훨씬 더!
break
, 루프의 동작을 극적으로 변경하여 코드가 달성하려는 내용뿐만 continue
아니라 return
루프가 작동하는 방식도 이해하도록 합니다. 이제 루프를 보다 간결하고 읽기 쉬운 코드로 변환하는 방법을 살펴보겠습니다.
코딩을 시작해 보세요!
우리는 기사로 작업할 것입니다. 기사에는 제목, 작성자 및 여러 태그가 있습니다.private class Article {
private final String title;
private final String author;
private final List<String> tags;
private Article(String title, String author, List<String> tags) {
this.title = title;
this.author = author;
this.tags = tags;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public List<String> getTags() {
return tags;
}
}
각 예제에는 루프를 사용하는 기존 솔루션과 Java 8의 새로운 기능을 사용하는 솔루션이 포함됩니다. 첫 번째 예제에서는 "Java" 태그가 있는 컬렉션의 첫 번째 기사를 찾으려고 합니다. 루프를 사용한 솔루션을 살펴보겠습니다.
public Article getFirstJavaArticle() {
for (Article article : articles) {
if (article.getTags().contains("Java")) {
return article;
}
}
return null;
}
이제 Stream API의 작업을 사용하여 문제를 해결해 보겠습니다.
public Optional<Article> getFirstJavaArticle() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.findFirst();
}
정말 멋지지 않나요? 먼저 이 작업을 사용하여 filter
"Java" 태그가 있는 모든 기사를 찾은 다음 findFirst()
첫 번째 항목을 가져오는 데 사용합니다. 스트림은 게으르고 필터는 스트림을 반환하므로 이 접근 방식은 첫 번째 일치 항목을 찾을 때까지 요소만 처리합니다. 이제 첫 번째 기사 대신 "Java" 태그가 지정된 모든 기사를 가져오겠습니다. 먼저 루프를 사용하는 솔루션입니다.
public List<Article> getAllJavaArticles() {
List<Article> result = new ArrayList<>();
for (Article article : articles) {
if (article.getTags().contains("Java")) {
result.add(article);
}
}
return result;
}
스트림 작업을 사용하는 솔루션입니다.
public List<Article> getAllJavaArticles() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.collect(Collectors.toList());
}
collect
이 예에서는 컬렉션을 선언하고 일치하는 항목을 명시적으로 추가하는 대신 결과 스트림을 단축하는 작업을 사용했습니다 . 여태까지는 그런대로 잘됐다. Stream API를 정말 빛나게 만드는 예제를 살펴볼 시간입니다. 모든 기사를 작성자별로 그룹화해 보겠습니다. 평소와 같이 루프를 사용하여 문제를 해결하는 것으로 시작합니다.
public Map<String, List<Article>> groupByAuthor() {
Map<String, List<Article>> result = new HashMap<>();
for (Article article : articles) {
if (result.containsKey(article.getAuthor())) {
result.get(article.getAuthor()).add(article);
} else {
ArrayList<Article> articles = new ArrayList<>();
articles.add(article);
result.put(article.getAuthor(), articles);
}
}
return result;
}
스트림 작업을 사용하여 이 문제에 대한 명확한 해결책을 찾을 수 있습니까?
public Map<String, List<Article>> groupByAuthor() {
return articles.stream()
.collect(Collectors.groupingBy(Article::getAuthor));
}
놀라운! 연산 groupingBy
과 메소드 참조를 사용하여 getAuthor()
깨끗하고 읽기 쉬운 코드를 얻습니다. 이제 컬렉션에 사용된 나머지 태그를 찾아보겠습니다. 루프 예제부터 시작해 보겠습니다.
public Set<String> getDistinctTags() {
Set<String> result = new HashSet<>();
for (Article article : articles) {
result.addAll(article.getTags());
}
return result;
}
이제 스트림 작업을 사용하여 이 문제를 어떻게 해결할 수 있는지 살펴보겠습니다.
public Set<String> getDistinctTags() {
return articles.stream()
.flatMap(article -> article.getTags().stream())
.collect(Collectors.toSet());
}
시원한! flatmap
태그 목록을 단일 결과 스트림으로 평면화한 다음 이를 사용하여 collect
반환 세트를 만드는 데 도움이 됩니다.
GO TO FULL VERSION