자바의 함수식 프로그래밍은 다음과 같다.

19634 단어 tutorialsjava

자바 개발자라면, 위의 특색 있는 그림 세션과 비슷한 코드를 적어도 한 번은 보셨을 거라고 믿습니다.위 코드 세션의 코드는 자바로 함수식 프로그래밍을 실현하는 예입니다. 요청한 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의 함수 인터페이스 주석의 동작은 다음과 같습니다.
  • 추상적인 방법만 있다.
  • 추상적인 방법 하나만 있으면 여러 가지 방법이 있다.
  • 유형에만 추가할 수 있습니다.
  • 우리는 lambda 표현식, 방법 인용 또는 구조 함수 인용을 사용하여 함수 인터페이스를 만들 수 있다.
  • 우리는 정의할 필요가 없다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 방법과 선택 수락forEachifPresent 방법은 수락Consumer<T>이 어떤 내용도 되돌려주지 않습니다.

    반응 라이브러리


    두 개의 가장 유행하는 자바 리액션 라이브러리RxJavaReactor는 모두 자바 8 Streams API를 기반으로 하고 코드에서도 함수 인터페이스를 사용한다는 것을 의미한다.
    만약 우리가 Reactor’s Flux API documentationRxJava’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://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
  • https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

  • https://www.amitph.com/java-method-and-constructor-reference/#:~:text=Constructor%20Reference%20is%20used%20to,assign%20to%20a%20target%20type .
  • https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
  • https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
  • http://reactivex.io/RxJava/javadoc/
  • https://projectreactor.io/docs/core/release/api/
  • 좋은 웹페이지 즐겨찾기