목록전체 글 (115)
도당탕탕
Java는 C나 C++에 비해 안전한 언어라고 할 수 있다. 하지만 우리가 작성한 클래스의 클라이언트에서 의도적이든, 실수로든 불변성을 파괴할 수 있음을 명심해야 한다. 첫 번째 가능한 공격 아래 코드는 클라이언트가 불변성을 파괴할 수 있는 예시이다. public final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { if (start.compareTo(end) > 0) throw new IllegalArgumentException(start+"after"+end); this.start = start; this.end = end; } public Date start(..
메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바란다. 예컨대 인덱스 값이 음수이면 안되고, 객체 참조는 null이면 안된다. 이런 제약은 반드시 문서화해야 하며 메서드 몸체가 시작되기 전에 검사해야 한다. 즉 오류는 가능한 한 빨리 잡아야 한다. 라는 일반 원칙의 한 사례이기도 하다. 오류를 잡지 못하면 해당 오류를 감지하기 어려워지고, 감지하더라도 오류의 발생 지점을 찾기 어려워진다. 매개변수 검사 방법 매개변수 검사를 제대로 하지 못하면 몇 가지 문제가 생길 수 있다. 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다. 더 나아가 잘못된 값을 반환해 메서드와는 관련 없는 오류를 낼 수 있다. 즉 매개변수 검사에 실패하면 실패 원자성을 어기는 결과를 낳을 수 있다...
public static void main(String[] args) { primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE)) .filter(mersenne -> mersenne.isProbablePrime(50)) .limit(20) .forEach(System.out::println); } static Stream primes() { return Stream.iterate(TWO,BigInteger::nextProbablePrime); } 위 작업에 parallel() 함수를 추가한다고 해서, 성능이 개선되지는 않는다. 실제로 parallel() 함수를 추가하면 2배 정도 시간이 소요된다. pipeline을 병렬화 하는 작업은 Stream.iter..
원소 시퀀스, 즉 일련의 원소를 반환하는 메서드는 수없이 많다. 자바 7까지는 이런 메서드의 반환 타입으로 다음과 같은 타입을 사용했다. Collection, Set, List E[]와 같은 배열 Iterable 인터페이스 즉 기본인 컬렉션 인터페이스를 사용했다. for-each문에서만 쓰이거나 반환된 원소 시퀀스가 일부 Collection 메서드를 구현할 수 없을 때는 Iterable 같은 인터페이스를 사용했다. 아니면 성능이 민감한 상황에서는 배열을 썼다. 하지만 자바8에 스트림이 나오면서 다음과 같은 선택들이 더 복잡해지게 되었다. 스트림 문제 스트림 문제는 다음과 같다. 스트림은 반복을 지원하지 않는다. 따라서 스트림과 반복을 알맞게 조합해야 좋은 코드가 나온다. API를 스트림만 반환하도록 짜놓..
스트림 패러다임은 계산을 일련의 transformation으로 구조화하는 것인데, 여기서 transformation은 이전 단계의 결과에 대해 항상 pure function 성질을 유지해야 한다. pure function 이란 결과가 항상 input에만 의존하는 함수를 말한다. 이를 달성하기 위해서는, stream 작업도중에 중간 결과나 최종 결과들이 side effect가 없어야 한다. side effect는 외부 상태를 변경하거나 예상치 못한 에러가 발생하는 상황을 말한다. forEach 작업은 항상 stream의 상태를 확인하기 위해서만 사용할 것 다음 코드는 각각의 단어에 대한 빈도수 테이블을 만드는 코드이다. Map freq = new HashMap(); try(Stream words = new..
스트림 API는 다량의 데이터 처리 작업을 돕고자 자바 8에서 추가되었다. 이 API가 제공하는 추상 개념 중 핵심은 다음과 같이 두 가지이다. 스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다. 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다. 즉 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산으로 끝나며, 그 사이에 하나 이상의 중간 연산이 있을 수 있다. 각 중간 연산은 스트림을 어떠한 방식으로 변환한다. 중간 연산들은 모두 한 스트림을 다른 스트림으로 변환하는데, 변환된 스트림의 원소 타입은 변환 전 스트림의 원소 타입과 같을 수도 있고 다를 수도 있다. 종단 연산은 마지막 중간 연산이 내놓은 스트림에 최후의 연산을 가한다. 즉 원소를 정렬해 컬렉션에 담거나, 특..
LinkedHashMap 클래스에 있는 removeEldestEntry 함수를 오버라이딩 해서, 클래스를 캐시로 사용할 수 있다. 이 함수는 put 메서드가 호출될 때마다 호출이 되고, true를 반환할 경우 가장 오래된 데이터를 삭제되도록 구현이 돼있다. protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 100; } 위 방법 대신 람다를 사용해서 LinkedHashMap 의 static factory or constructor에 function object를 전달해서 구현하는 것이 낫다. @FunctionalInterface interface EldestEntryRemovalFunction { boolean remove(Ma..
람다가 익명 클래스보다 나은 점 중 가장 큰 특징은 간결함이다. 하지만 람다보다도 더 간결하게 만드는 방법이 존재한다. 바로 메서드 참조를 통해 더 간결하게 만들 수 있다. 다음 코드를 보자 람다를 사용한 map.merge() 코드 //merge 매개변수 : key, value, BiFunction(? super value, ? super vlaue, ? extends value,) map.merge(key, 1, (count, incr) -> count + incr); 이 코드는 키가 맵 안에 없다면 키와 숫자 1을 매핑하고, 이미 있다면 기존 매핑 값을 증가 시킨다. 깔끔해 보이지만, 매개변수인 count와 incr은 크게 하는 일 없이 공간을 꽤 차지한다. 즉 이 람다는 두 인수의 합을 단순히 반환..