Skip to content

Latest commit

 

History

History
394 lines (238 loc) · 9.5 KB

3-9. 이론 - java8.md

File metadata and controls

394 lines (238 loc) · 9.5 KB

함수형 프로그래밍 (Functional Programming)

동시성 이슈

멀티CPU 상황에서 동시성 이슈를 해결하면서 안정적인 소프트웨어를 개발하는 것에 대한 중요성이 높아졌다.
데이터의 상태를 변경하는 객체지향 프로그래밍 방식으로 동시성 문제를 해결하는 데는 한계가 있다.

데이터 관리에 따른 부담

대용량 데이터를 다루는 작업이 점점 더 많아지고 있다. 대용량 데이터를 처리할 때 데이터를 객체로 변환하는데 따른 부담이 크다.
대용량 데이터를 처리할 수 있는 효율적인 데이터 구조와 데이터 연산이 필요하다.

함수형 프로그래밍은 모듈화되어있다.

객체 단위는 가장 작은 단위의 모듈화가 아니다(메서드, 필드). 함수형 프로그래밍의 함수를 모듈화할 경우 수많은 곳에서 재사용할 수 있다.
함수형 프로그래밍은 더 유용하고, 재사용이 편리하고, 구성이 용이하고, 테스트하기 더 간편한 추상화를 할 수 있다.

더 빠르게 작업해야한다.

소프트웨어 개발 흐름은 점점 더 빠른 결과물을 확인할 수 있기를 기대하는 방향으로 변화되고있다.
객체에 대한 모델링에 많은 시간을 투자하기보다 사용자 요구사항에 대해서 최소한으로 충분한 수준을 유지하면서 동시에 변화에 대해서도 유연하게 대응하는데 도움을 준다.

함수형 프로그래밍은 단순함으로의 복귀다.

요구사항을 구현하기 위해 선택된 방식에서의 복잡성을 단순화할 수 있을 것으로 기대한다.

복잡한 문제를 작은 단위로 분리해 해결하는 능력은 프로그래머에게 특히 중요하다.

함수형 프로그래밍을 학습하면서 문제에 접근하는 방법, 문제를 작은 단위로 쪼개는 방법, 설계 과정, 프로그래밍 하는 순서에 대해 새로운 시각을 배울 수 있다.

함수형 프로그래밍 방식을 학습하면 현재 프로그래밍 스타일을 개선해 더 깔끔한 코드(clean code)를 구현하는데 도움을 받을 수 있다.

명령형 프로그래밍 vs 선언형 프로그래밍

변경 불가능한 값(immutable value)을 활용

값이 변경되는 것을 허용하면 멀티 스레드 프로그래밍이 힘들다.
값을 변경할 수 없는 경우 프로그램의 정확성을 높여 버그 발생의 가능성을 줄인다.

1등 시민으로서의 함수

함수형 프로그래밍에서는 함수가 1등 시민의 역할을 한다.
함수를 1등 시민으로 활용이 가능할 경우 함수를 함수의 인자로 받거나 함수의 반환값으로 활용하는 것이 가능하다.

람다와 클로저

람다는 익명 함수의 다른 표현이다. 즉 함수는 함수인데 이름이 없는 경우를 말한다.

고계함수

함수를 인자로 받을 수 있고, 함수를 리턴할 수 있다.

부수효과

함수형 프로그래밍의 가장 큰 핵심적인 특징은 `side effect를 만들지 않는 것`이다.

람다(lambda)

람다와 클로저

람다는 익명함수의 다른 표현이다. 즉 함수는 함수인데 이름이 없는 경우를 의미한다.

Collection의 모든 값을 출력

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6;)
  
  for(int number : numbers) {
    System.out.println(number);
  }

람다가 없던 시절

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);

numbers.forEach(new Consumer<Integer>() {
  public void accept(Integer value) {
    System.out.println(value);
  }
})

람다 활용

numbers.forEach((Integer value) -> System.out.println(value));
numbers.forEach(value -> System.out.println(value)); // Type 추론이 가능해 Type 생략 가능
numbers.forEach(System.out::println); // :: 연산자 활용 가능
= numbers.forEach(x -> System.out.println(x));

람다 문법

input arguments -> body

람다 실습 1 - 익명 클래스를 람다로 전환

람다 실습 2 - 람다를 활용해 중복 제거

익명클래스

프로그램에서 일시적으로 한번만 사용되고 버려지는 객체
일시적으로 사용, 재사용되지 않는다. 
UI 이벤트 처리, 스레드 객체
부모 클래스 [필드|변수] = new 부모클래스(매개값) {
  
};

Optional 실습

출처 https://www.baeldung.com/java-optional

빈 Optional 객체 생성

Optional<String> empty = Optional.empty();
assertFalse(empty.isPresent());
String name = "jisu";
Optional<String> opt = Optional.of(name);
assertTrue(opt.isPresent());

of() 메서드에 전달된 인수는 null일 수 없다.

Optional.of(null); // NullPointerException

null이 예상될 경우 ofNullable() 이용

Optional<String> opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());

값 존재 확인 isPresent() isEmpty()

메소드에서 반환되거나 생성된 Optional 객체가 있는 경우 isPresent()메서드를 사용해서 객체에 값이 있는지 확인할 수 있다.

Optional<String> opt = Optional.of("jisu");
assertTrue(opt.isPresent());

opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());

Java11부터는 isPresent의 반대 작업을 isEmpty로 수행

Optional<String> opt = Optional.of("jisu");
assertFalse(opt.isEmpty());

ifPresent()를 사용한 조건부 동작

null이 아닌 것으로 확인된 경우 래핑된 값에 대해 일부 코드 실행 가능

//as-is
if(name != null) {
  System.out.println(name.length());
}

//to-be
Optional<String> opt = Optional.of("jisu");
opt.ifPresent(name -> System.out.println(name.length()));

orElse()의 기본값

Optional 인스턴스 내부에 래핑된 값을 검색하는데 사용되며 기본으로 하나의 매개변수를 사용한다. orElse() 메서드는 래핑된 값이 있으면 반환하고 그렇지 않으면 인수를 반환한다.

String nullName = null;
String name = Optional.orNullable(nullName).orElse("jisu");
assertThat(name).isEqualTo("jisu");

orElseGet()의 기본값

orElse()와 유사한데, Optional값이 없으면 반환할 값을 가져오는 대신 Producer기능 인터페이스를 사용한다.

String name = Optional.ofNullable(null).orElseGet(()->"jisu");
assertThat(name).isEqualTo("jisu");

orElse()와 orElseGet의 차이

Using orElseGet:
Using orElse:
Getting default value...

orElse는 값 of에 값이 있어도 orElse에 담은 기본 객체를 생성한다. 기본 객체를 만드는데 큰 비용이 든다면 문제가 될 수 있다.

orElseThrow()

부재값을 처리하기 위해 래핑된 값이 없을 때 기본값을 반환하는 대신 예외를 발생시킨다.

Optional.ofNullable(nullName).orElseThrow(IllegalArgumentException::new);

java10에서는 인자가 없으면 NoSuchElementException이 발생한다

Optional.ofNullable(nullName).orElseThrow();

get()

래핑된 값을 검색하는 마지막 방법

  • Optional의 목표에 어긋나기때문에 향후 릴리스에서 사용되지 않을 예정

  • null케이스를 준비해 명시적으로 처리할 수 있는 다른 변경을 사용하는 것이 좋다.

filter()

매핑된 값에 대해 인라인 테스트 실행 가능

Integer year = 2016;
Optional<Integer> yearOptional = Optional.of(year);
boolean is2016 = yearOptional.filter(y->y == 2016).isPresent();
assertTrue(is2016);
boolean is2017 = yearOptional.filter(y->y == 2017).isPresent();
assertFalse(is2017);

optional이 없는 경우와 비교

//as-is
public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;

    if (modem != null && modem.getPrice() != null 
      && (modem.getPrice() >= 10 
        && modem.getPrice() <= 15)) {

        isInRange = true;
    }
    return isInRange;
}

//to-be
public boolean priceIsInRange2(Modem modem2) {
     return Optional.ofNullable(modem2)
       .map(Modem::getPrice)
       .filter(p -> p >= 10)
       .filter(p -> p <= 15)
       .isPresent();
 }

map

맵과 필터 연결하여 더 강력한 작업 수행

사용자가 입력한 비밀번호의 정확성 확인하고싶다고 가정

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {
    String password = " password ";
    Optional<String> passOpt = Optional.of(password);
    boolean correctPassword = passOpt.filter(
      pass -> pass.equals("password")).isPresent();
    assertFalse(correctPassword);

    correctPassword = passOpt
      .map(String::trim)
      .filter(pass -> pass.equals("password"))
      .isPresent();
    assertTrue(correctPassword);
}

flatMap

map은 래핑되지 않은 경우에만 값을 변환하는 반면 flatMap은 래핑된 값을 가져와 변환하기 전에 래핑 해제한다.

@Test
public void givenOptional_whenFlatMapWorks_thenCorrect2() {
    Person person = new Person("john", 26);
    Optional<Person> personOptional = Optional.of(person);

    Optional<Optional<String>> nameOptionalWrapper  
      = personOptional.map(Person::getName);
    Optional<String> nameOptional  
      = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
    String name1 = nameOptional.orElse("");
    assertEquals("john", name1);

    String name = personOptional
      .flatMap(Person::getName)
      .orElse("");
    assertEquals("john", name);
}