자바의 함수식 프로그래밍은 다음과 같다.
자바 개발자라면, 위의 특색 있는 그림 세션과 비슷한 코드를 적어도 한 번은 보셨을 거라고 믿습니다.위 코드 세션의 코드는 자바로 함수식 프로그래밍을 실현하는 예입니다. 요청한
List<String>
을 필터해서 다른 List<String>
로 변환합니다.본고에서 자바의 API를 사용하여 함수식 프로그래밍 코드를 작성하는 방법을 소개할 것입니다.마지막으로 자바로 함수식 프로그래밍 스타일을 어떻게 실현하는지 알아보기 위해 흐르는 API를 작성할 것입니다.
Java의 함수 프로그래밍
자바의 함수식 프로그래밍은 이미 오랫동안 존재해 왔다.갑골문이 2014년 자바8을 발표했을 때lambda expression를 내놓았는데 이것은 자바 함수식 프로그래밍의 핵심 기능이다.
자바에서 명령문 서열을 사용하는 것과 함수식을 사용하는 것 사이의 차이를 설명하는 예를 보겠습니다.
List<String> stringList = Arrays.asList("Hello", "World", "How", "Are", "You", "Today");
// imperative declaration
List<String> filteredList = new ArrayList<>();
for (String string: stringList) {
if (string.equals("Hello") || string.equals("Are")) {
filteredList.add(string);
}
}
List<String> mappedList = new ArrayList<>();
for (String string: filteredList) {
mappedList.add(string + " String");
}
for (String string: mappedList) {
System.out.println(string);
}
명령 스타일
List<String> stringList = Arrays.asList("Hello", "World", "How", "Are", "You", "Today");
//functional style
stringList.stream()
.filter(s -> s.equals("Hello") || s.equals("Are"))
.map(s -> s + " String")
.forEach(System.out::println);
기능성 스타일보시다시피 두 단락의 코드는 같은 결과를 실현했지만 차이가 현저하다.함수식 코드에 비해 명령식 성명 코드는 큰 괄호가 많고, 많이 자라서 읽기가 더욱 어렵다.
기능 인터페이스 설명
함수식 프로그래밍이 자바에서 어떻게 작동하는지 이해하기 위해서는 먼저 자바8SDK
@FunctionalInterface
에 포함된 주석을 보아야 한다.우리는 Java API documentation site에서 그것을 볼 수 있다.API 문서에서 볼 수 있는 Java의 함수 인터페이스 주석의 동작은 다음과 같습니다.
Interface
. 왜냐하면 컴파일러는 함수 인터페이스를 만족시키는 모든 인터페이스를 함수 인터페이스로 간주하기 때문이다.함수 인터페이스 클래스 만들기
이제 우리는 기능 인터페이스의 모든 내용을 알게 되었고, 우리는 스스로 그것을 만들 수 있다.
우리는 먼저
@FunctionalInterface
라는 모델을 만들었다.package com.example.functional.programming.model;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
this.name = name;
}
public static Person createClassExampleFromMethodReference(String name) {
return new Person(name);
}
함수 인터페이스에 대해 우리는 Person
클래스를 만들 것이다.
package com.example.functional.programming.intf;
import com.example.functional.programming.model.Person;
@FunctionalInterface
public interface PersonFunctionalInterface {
Person createPerson(String name);
default String getDefaultMethodString() {
return "Default Method";
}
}
인터페이스에는 두 가지 방법이 있지만 추상적인 방법만 있기 때문에 PersonFunctionalInterface
류는 함수 인터페이스로 유효합니다.그러나 우리가 추상적인 방법을 정의한 것은 다음과 같다.
package com.example.functional.programming.intf;
import com.example.functional.programming.model.Person;
@FunctionalInterface
public interface PersonFunctionalInterface {
Person createPerson(String name);
String mapStringToObject(String str);
default String getDefaultMethodString() {
return "Default Method";
}
}
오류가 발생합니다.[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /D:/Project/functional/src/main/java/com/example/functional/programming/intf/PersonFunctionalInterface.java:[5,1] Unexpected @FunctionalInterface annotation
com.example.functional.programming.intf.PersonFunctionalInterface is not a functional interface
multiple non-overriding abstract methods found in interface com.example.functional.programming.intf.PersonFunctionalInterface
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.105 s
[INFO] Finished at: 2020-09-19T10:34:45+07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project functional-programming: Compilation failure
[ERROR] /D:/Project/functional/src/main/java/com/example/functional/programming/intf/PersonFunctionalInterface.java:[5,1] Unexpected @FunctionalInterface annotation
[ERROR] com.example.functional.programming.intf.PersonFunctionalInterface is not a functional interface
[ERROR] multiple non-overriding abstract methods found in interface com.example.functional.programming.intf.PersonFunctionalInterface
기능 인터페이스 사용
익명 클래스
먼저 익명류에 대해 알아보자.Javadocumentation:
“Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.”
기본적으로, 익명 클래스에 대해, 우리는 우리가 만든 인터페이스를 실현하는 클래스를 정의할 필요가 없다.우리는 이름이 없는 클래스를 만들어서 변수에 저장할 수 있다.
익명 클래스를 예로 들겠습니다.
@Test
void declareAnonymousClass() {
PersonFunctionalInterface anonClassExample = new PersonFunctionalInterface() {
@Override
public Person createPerson(String name) {
return new Person(name);
}
};
assert (anonClassExample.createPerson("Hello, World").getName().equals("Hello, World"));
}
우리가 여기서 한 것은 익명 클래스를 만들었는데, 그 종류는 PersonFunctionalInterface
이고, 이름은 PersonFunctionalInterface
이다.우리는 추상적인 방법을 다시 쓰기
anonClassExample
하기 때문에, 이 방법을 호출할 때, 새로운 Person 대상을 되돌려주고 이름을 붙인다.우리가
createPerson
을 호출했을 때, 우리는 기본적으로 'Hello, World' 라는 새로운 Person 대상을 만들었을 뿐이다.함수 인터페이스를 사용하여 익명 클래스 만들기
우리가 만든 함수 인터페이스에 익명 클래스
anonClassExample.createPerson(“Hello, World”)
를 만들 수 있습니다. @Test
void interfaceExample() {
PersonFunctionalInterface normalAnonymousClass = new PersonFunctionalInterface() { // create normal anonymous class
@Override
public Person createPerson(String name) {
return new Person(name);
}
};
PersonFunctionalInterface interfaceExampleLambda =
name -> new Person(name); // create anonymous class by lambda
PersonFunctionalInterface interfaceExampleMethodReference =
Person::createClassExampleFromMethodReference; // create anonymous class by method reference
PersonFunctionalInterface interfaceExampleConstructorReference =
Person::new; // create anonymous class by constructor reference
// assert that every anonymous class behave the same
assert(normalAnonymousClass
.createPerson("Hello, World").getName().equals("Hello, World"));
assert(interfaceExampleLambda
.createPerson("Hello, World").getName().equals("Hello, World"));
assert(interfaceExampleMethodReference
.createPerson("Hello, World").getName().equals("Hello, World"));
assert(interfaceExampleConstructorReference
.createPerson("Hello, World").getName().equals("Hello, World"));
assert(normalAnonymousClass.getDefaultMethodString().equals("Default Method"));
assert(interfaceExampleLambda.getDefaultMethodString().equals("Default Method"));
assert(interfaceExampleMethodReference.getDefaultMethodString().equals("Default Method"));
assert(interfaceExampleConstructorReference.getDefaultMethodString().equals("Default Method"));
}
우리는 방금 기능 인터페이스를 실현했다!위의 코드에서, 우리는 서로 다른 방식으로 세 개의 익명 클래스를 만들었다.익명 클래스의 행동은 lambda 표현식, 방법 인용, 구조 함수 인용으로 함수 인터페이스를 만들 수 있다는 것을 기억하십시오.
행동이 같은 익명 클래스를 만들었는지 확인하기 위해서 인터페이스의 모든 방법을 단언합니다.
Java 8의 내장 함수 인터페이스
자바8은
PersonFunctionalinterface
패키지에 내장된 함수 인터페이스 클래스가 많은데 its documentation에서 볼 수 있습니다.본 논문에서 가장 자주 사용하는 네 가지 기능 인터페이스만 설명하지만, 더 많은 내용에 관심이 있으면 위에서 언급한 자바API 문서에서 읽을 수 있습니다.
java.util.function
: 함수 인터페이스로 대상을 받아들이지만 내용을 되돌려주지 않습니다.Consumer<T>
: 어떤 내용도 받아들이지 않고 대상을 되돌려주는 기능 인터페이스.Producer<T>
: 대상을 받아들이고 브리 값의 함수 인터페이스를 되돌려줍니다.Predicate<T>
: 한 대상을 받아들이고 다른 대상의 함수 인터페이스를 되돌려줍니다.상용
만약 당신이 자바를 자주 사용하여 개발을 진행한다면, 당신은 이미 기능 인터페이스의 개념을 만났을 것이다.
스트리밍 및 옵션 API
아래 코드에서 보듯이 자바의 흐름 API는 기능 인터페이스를 대량으로 사용합니다.
@Test
void commonFunctionalInterface() {
Stream.of("Hello", "World", "How", "Are", "you")
.filter(s -> s.equals("Hello") || s.equals("Are"))
.map(s -> s + " String")
.forEach(System.out::println);
Optional.of("Hello")
.filter(s -> s.equals("Hello") || s.equals("Are"))
.map(s -> s + " String")
.ifPresent(System.out::println);
}
Function<T, R>
방법은 매개 변수filter
기능 인터페이스가 있다.보시다시피 이 방법은 aPredicate<T>
를 받아들여 aString
를 생성한다.boolean
방법은 map
를 매개 변수로 사용한다.그것은 받아들여져서 Function<T, R>
되돌아왔다. String
흐름 중String
방법과 선택 수락forEach
중ifPresent
방법은 수락Consumer<T>
이 어떤 내용도 되돌려주지 않습니다.반응 라이브러리
두 개의 가장 유행하는 자바 리액션 라이브러리RxJava와 Reactor는 모두 자바 8 Streams API를 기반으로 하고 코드에서도 함수 인터페이스를 사용한다는 것을 의미한다.
만약 우리가 Reactor’s Flux API documentation와 RxJava’s Observable API documentation를 보면, 우리는 그것들의 많은 방법이 함수 인터페이스를 받아들이는 것을 볼 수 있다.
우리만의 흐름 API 만들기
이제 기능 인터페이스를 만들고 사용하는 방법을 알았습니다. 기능 인터페이스를 어떻게 실현하는지 알아보기 위해 저희만의 흐르는 API를 만들어 보겠습니다.
물론, 우리의 흐르는 API는 자바보다 훨씬 간단하다.
package com.example.functional.intf;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class SimpleStream<T> {
private List<T> values;
public SimpleStream(T... values) {
this.values = Arrays.asList(values);
}
public SimpleStream(List<T> values) {
this.values = values;
}
public SimpleStream<T> filter(Predicate<T> filter) {
List<T> returnValueList = new ArrayList<>();
for (T value : values) {
if (filter.test(value)) {
returnValueList.add(value);
}
}
this.values = returnValueList;
return this;
}
public SimpleStream<T> map(Function<T, T> function) {
List<T> returnValueList = new ArrayList<>();
for (T value : values) {
returnValueList.add(function.apply(value));
}
this.values = returnValueList;
return this;
}
public void forEach(Consumer<T> consumer) {
for (T value : values) {
consumer.accept(value);
}
}
public List<T> toList() {
return this.values;
}
}
및 테스트 과정: @Test
void implementingFunctionalInterface() {
List<String> stringsFromSimpleStream = new SimpleStream<>("Hello", "World", "How", "Are", "you")
.filter(s -> s.equals("Hello") || s.equals("Are"))
.map(s -> s + " String")
.toList();
assert(stringsFromSimpleStream.size() == 2);
assert(stringsFromSimpleStream.get(0).equals("Hello String"));
assert(stringsFromSimpleStream.get(1).equals("Are String"));
new SimpleStream<>(stringsFromSimpleStream)
.forEach(System.out::println);
}
자, 우리 하나씩 방법을 토론합시다.건조사
우리는 두 개의 구조 함수를 만들었는데 하나는 모방
String
API의 구조 함수이고, 다른 하나는 Stream.of()
를 List<T>
로 바꾸는 구조 함수이다.필터
이 방법에서 우리는
SimpleStream<T>
를 매개 변수로 받아들인다. Predicate<T>
에는 Predicate<T>
라는 추상적인 매개 변수가 있는데 이것은 하나의 대상을 받아들여 브리 값을 생성하기 때문이다.테스트 유형을 살펴보겠습니다.
.filter(s -> s.equals("Hello") || s.equals("Are"))
이것은 우리가 익명 클래스를 만들어서 실현했다는 것을 의미한다test
.
Predicate<String> filter = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.equals("Hello") || s.equals("Are");
}
};
Predicate<T>
클래스에서 우리는 Filter 방법을 볼 수 있다.
public SimpleStream<T> filter(Predicate<T> filter) {
List<T> returnValueList = new ArrayList<>();
for (T value : values) {
if (value.equals("Hello") || value.equals("Are")) {
returnValueList.add(value);
}
}
this.values = returnValueList;
return this;
}
지도.
맵 방법에서 우리는
SimpleStream<T>
를 매개 변수로 받아들인다. 이것은 맵 방법이 함수 인터페이스를 받아들이고 이 인터페이스는 하나의 대상을 받아들여 하나의 대상을 생성한다는 것을 의미한다.우리는 테스트 수업에 다음과 같은 내용을 썼다.
.map(s -> s + " String")
이것은 익명 클래스 생성 Function<T, R>
과 같습니다.
Function<String, String> map = new Function<String, String>() {
@Override
public String apply(String s) {
return s + " String";
}
};
Function<T, R>
교육 과정에서 다음과 같은 내용을 확인할 수 있습니다. public SimpleStream<T> map(Function<T, T> function) {
List<T> returnValueList = new ArrayList<>();
for (T value : values) {
returnValueList.add(value + " String");
}
this.values = returnValueList;
return this;
}
프레치
SimpleStream<T>
방법은 forEach
를 매개 변수로 받아들인다. 이것은 어떤 내용도 되돌려주지 않고 대상을 받아들인다는 것을 의미한다.우리는 테스트 수업에 다음과 같은 내용을 썼다.
.forEach(System.out::println);
이것은 익명 클래스를 만드는 것으로 전환됩니다. Consumer<T>
Consumer<String> forEach = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
Consumer<T>
에서 우리는 SimpleStream<T>
방법을 다음과 같이 볼 수 있다.
public void forEach(Consumer<T> consumer) {
for (T value : values) {
System.out.println(value);
}
}
결론
2014년 자바8이 발표됨에 따라 우리는 자바에서 함수식 프로그래밍 스타일을 사용할 수 있다.자바에서 함수식 프로그래밍 스타일을 사용하는 것은 많은 장점이 있는데, 그 중 하나는 코드를 더욱 짧고 읽을 수 있게 하는 것이다.자바 개발자라면 자바 함수 프로그래밍의 실현을 이해하는 것이 필수적입니다!
본문을 읽어 주셔서 감사합니다!
본 문서에 사용된 GitHub 저장소는 다음과 같습니다.
( https://github.com/brilianfird/java-functional-programming )
리소스
https://www.amitph.com/java-method-and-constructor-reference/#:~:text=Constructor%20Reference%20is%20used%20to,assign%20to%20a%20target%20type .
Reference
이 문제에 관하여(자바의 함수식 프로그래밍은 다음과 같다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/brilianfird/functional-programming-in-java-explained-1gd2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)