라벨이 자바8인 게시물 표시

(자바8 - 함수형 프로그래밍 기법)

함수를 마치 일반값처럼 사용해서 인수로 전달하거나, 결과로 반환받거나, 자료구조에 저장할 수 있음을 의미한다. 일반 값처럼 취급할 수 있는 함수를 일급 함수 라고 한다. 바로 자바8이 이전버전과 구별되는 특징 중 하나가 일급 함수를 지원한다는 점이다. 자바8에서는 ::연산자로 메서드 레퍼런스를 만들거나 (int x) -> x + 1 같은 람다표현식으로 직접 함숫값을 표현해서 메소드를 함숫값으로 사용할 수 있다. 자바8에서는 다음과 같은 메서드 레퍼런스로 Integer.parsetInt를 저장 할 수 있다. Function<String, Integer> strToInt = Integer.parseInt 1. 고차원 함수 2. 커링 대부분 애플리케이션은 국제화를 지원해야 하는데 이때 단위 변환 문제가 발생할 수 있다. 보통 변환요소와 기준치 조정요소가 단위 변환 결과를 좌우한다. 다음은 섭씨를 화씨로 변환하는 공식이다. CtoF(x) = x*9/5 + 32 다음과 같은 패턴으로 단위를 표현할 수 있다. 1. 변환요소를 곱함 2. 기준치 조정요소를 적용 static double converter(double x, double y, double f) {   return x * f + b; } 위와같이 하면 인수에 변환요소와 기준치를 넣는 일은 귀찮은 일이며 오타도 발생하기 쉽다. 다음은 커링이라는 개념을 활용해서 한 개의 인수를 갖는 변환 함수를 생산하는 '팩토리' 를 정의하는 코드이다. static DoubleUnaryOperator curreidConverter(double f, double b) {  return (Double x) -> x * f +b; } 위 메소드에 변환요소(f)와 기준치 (b) 만 넘겨주면 우리가 원하는 작업을 수행할 함수가 반환된다. 예를들어, 다음은 팩토리를 이용해서 원하는 변환기를 생성하는 코드다. DoubleUnaryOpe...

(자바8) 쇼트서킷 평가

떄로는 전체 스트림을 평가하지 않더라도 결과를 반환할 수 있다. 예를들어, 여러 and 연산으로 연결된 커다란 불린 표현식을 평가한다고 가정하자. 표현식에서 하나라도 거짓이라는 결과가 나오면 나머지 표현식의 결과와 상관없이 전체 결과도 거짓이 된다. 이러한 상황을 쇼트서킷 이라고 부른다. allMatch, noneMatch, findFirst, findAny 등의 연산은 모든 스트림을 처리하지 않고도 결과를 반환할 수 있다. limit도 쇼트 서킷이다.

(자바 8) 바이너리 호환성, 소스 호환성, 동작 호환성

* 뭔가를 바꾼 이후에도 에러 없이 기존 바이너리가 실행될 수 있는 상황을 바이너리 호환성이라고 한다. (바이너리 실행에는 인증, 준비, 해석 등의 과정이 포함된다.) 예를들어 인터페이스에 메소드를 추가했을 때 추가된 메소드를 호출하지 않는 한 문제가 일어나지 않는데 이를 바이너리 호환성 이라고 한다. 간단히 말해, 소스호환성 이란 코드를 고쳐도 기존 프로그램을 성공적으로 재컴파일할 수 있음을 의미한다. 예를들어 인터페이스에 메소드를 추가하면 소스 호환성이 아니다. 추가한 메소드를 구현하도록 클래스를 고쳐야 하기 때문이다. 마지막으로 동작호환성 이란 코드를 바꾼 다음에도 같은 입력값이 주어지면 프로그램이 같은 동작을 실행한다는 의미이다. 예를들어 인터페이스에 메소드를 추가하면 프로그램에서 추가된 메소드를 호출할 일이 없으므로(혹은 우연히 구현 클래스가 이를 오버라이드했을 수도 있다) 동작 호환성은 유지된다.

(자바 8) 디폴트 메소드

* 디폴트 메소드는 주로 라이브러리 설계자들이 사용한다. * 디폴트 메소드가 없던 시절에는 인터페잇에 메소드를 추가하면서 여러 문제가 발생했다. 인터페이스에 새로 추가된 메소드를 구현하도록 인터페이스를 구현하는 기존 클래스를 고쳐야 했기 때문이었다. 본인이 직접 인터페이스와 이를 구현하는 클래스를 관리할 수 있는 상황이라면 이 문제를 어렵지 않게 해결할 수 있었지만 인터페이스를 대중에게 공개했을 떄는 상황이 다르다. 그래서 디폴트 메소드가 탄생한 것이다. 우리가 라이브러리 설계자라면 기존 구현을 고치지 않고도 인터페이스를 바꿀 수 있으므로 디폴트 메소드를 잘 이해하는 것이 중요하다. * 우리가 만드는 인터페이스에도 디폴트 메소드를 추가할 수 있따. <디폴트 메소드 활용패턴> 1. 선택형 메소드 ->자바8이전에는 Iterator에 remove메소드가 비어있었다. 하지만  자바 8의 Iterator인터페이스는 remove메소드를 정의한다. 기본 구현이 제공되므로 Iterator인터페이스를 구현하는 클래스는 빈 remove메소드를 구현할 필요가 ㅇ벗어졌고, 불피요한 코드를 줄일 수 있다. 2. 동작 다중 상속 ->자바에서 클래스는 한 개의 다른 클래스만 상속 할 수 있지만 인터페이스는 여러개 구현할 수 있다. 디폴트 메소드를 이용하면 다중상속이 가능해진다. * 다른 클래스나 인터페이스로부터 같은 시그니처를 갖는 메소드를 상속받을 때 <알아야 할 세 가지 해결 규칙> 1. 클랫가 항상 이긴다. 클래스나 슈퍼클래스에서 정의한 메소드가 디폴트 메소드보다 우선권을 갖는다. 2. 1번 규칙 이외의 상황에서는 서브 인터페이스가 이긴다. 상속관계를 갖는 인터페이스에서 같은 시그니처를 갖는 메소드를 정의할 때는 서브인터페이스가 이긴다. 즉, B가 A를 상속받는다면  B가 A를 이긴다. 3. 여전히 디폴트 메소드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속받는 클래...

(자바 8) 실행 어라운드 패턴.

자원처리 (예를들면 데이터베이스의 파일처리)에 사용하는 순환패턴은 자원을 열고, 처리한 다음에, 자원을 닫는 순서로 이루어진다. 설정과 정리과정은 대부분 비슷하다. 즉, 실제자원을 처리하는 과정은 설정과 정리 두 과정이 둘러싼 형태를 갖는다. 예를들어) 작업 A, 작업 B를 한다치면 1. 초기화/준비코드 -> 작업 A -> 정리/마무리코드 2. 초기화/준비코드 -> 작업 B -> 정리/마무리코드 즉, 이러한 것을 실행어라운드 패턴이라 부른다. 그러나 작업 A, 작업 B를 실행하기 위해서는 위와같이 중복되는 준비 코드와 정리코드가 발생한다. 이를 줄이기 위한 것이다. 이 패턴은 총 4단계를 거친다. 1단계 : 동작파라미터화를 기억하라. 2단계 : 함수형 인터페이스를 이용해서 동작 전달 3단계 : 동작 실행! 4단계 : 람다 전달 public static String processFile() throws IOException{    try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){      return br.readLine();    } } 1단계 : 동작파라미터화를 기억하라. -> 위 코드는 파일에서 한 번에 한 줄만 읽을 수 있다. 만약 한 번에 두 줄을 읽거나 가장 자주 사용되는 단어를 반환하려면 어떻게 해야 할까? processFile의 동작을 파라미터화하는 것이다. 람다를 이용해서 동작을 전달할 수 있다. String result = processFile((BufferedReader br) -> br.readLine() + br.readLine()); 2단계 : 함수형 인터페이스를 이용해서 동작 전달 함수형 인터페이스 자리에 람다를 사용할 수 있다. 따라서 BufferedReader -> String 과 IOE...

(자바 8) null 대신 Optional

예를들어 어떤사람이 차를 소유하고 있지 않다면 Person 클래스의 car 변수는 null를 가져야 한다. 하지만 새로운 Optional 을 이용할 수 있으므로 null를 할당하는 것이 아니라 변수형식을 Optional<Car>로 설정할 수 있다. * Optional 이란? Optional<T> 클래스 (java.util.Optional)는 값의 존재나 부재 여부를 표현하는 컨테이너 클래스다. 예를들어 Optional<Dish> 에서느 요리명이 null 인지 검사할 필요가 없었다. menu.stream() .filter(Dish::isVegetarian) .findAny() .ifPresent(d -> System.out.println(d.getName());

(자바8) 스트림 활용

<필터링과 슬라이싱> 1. 프레디케이트로 필터링 -> filter 메소드는 프레디케이트(불린을 반환하는 함수)를 인수로 받아서 프레디케이트와 일치하는 모든 요소를 포함하는 스트림을 반환한다. 예) List<Dish> vegetarianMenu = menu.stream()                                                .filter(Dish::isVegetarian) //채식 요리인지 확인하는 메소드레퍼런스                                                . collect(toList()); 2. 고유요소 필터링 -> distinct라는 메소드 (hashCode, equals로 결정) List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 4, 5); numbers.stream() .filter(i -> i % 2==0) .distnct() .forEach(System.out::println); 3. 스트림축소 -> limit 4. 요소 건너뛰기 -> skip(n) <매핑> 스트림 API 의 map과 floatMap메소드는 특정 데이터를 선택하는 기능을 제공 List<Integer> dishNameLengths = menu.stream()           ...

(자바8) 스트림

* 많은 요소를 포함하는 커다란 컬렉션의 처리? ->성능을 높이려면 멀티커오 아키텍처를 활용해서 병렬로 컬렉션의 요소를 처리해야 한다. * 컬렉션과 스트림의 가장 큰 차이 -> 데이터를 언제 계산하느냐가 컬렉션과 스트림의 가장 큰 차이이다. 컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조. -> 반면 스트림은 이론적으로 요청할 때만 요소를 계산하는 고정된 자료구조. <스트림이란 무엇인가?> 스트림을 이용하면 멀티 스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리하는 것 * 스트림에 filter, map, limit, collect로 이어지는 일련의 데이터처리 연산을 적용한다. collect를 제외한 모든 연산은 서로 파이프라인을 형성할 수 있도록 스트림을 반환한다. <filter> 람다를 인수로 받아 특정 요소를 제외시킨다.  예) d -> d.getCalories() >300 <map> 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출한다. Dish::getName(람다표현식으로는 d -> d.getName()) <limit> 정해진 개수 이상의 요소가 스트림에 저장되지 못하게 스트림 크기를 축소한다. <collect> 스트림을 다른형식으로 변환한다. <외부반복과 내부 반복> 컬렉션은 외부반복, 스트림은 내부반복이다. 1.  컬렉션 for-each루프를 이용한 외부반복 예) List<String> names = new ArrayList<String>(); for(Dish d: menu){   names.add(d.getName()); } 2. 스트림 내부반복 List<String> names = menu.stream() ...