자바의 동시성 프로그래밍 자바는 동시성 프로그래밍을 지원하려고 항상 노력하였다. 처음부터 스레드, 동기화, wait/notify를 지원 자바5부터는 동시성 컬렉션인 java.util.concurrent 라이브러리와 실행자(Exeuctor) 프레임워크를 지원 자바7부터는 고성능 병렬 분해 프레임워크인 포크-조인(fork-join) 패키지 추가 자바8부터는 스트림의 parallel 메서드로 파이프라인을 병렬 실행할 수 있도록 지원 → 동시성 프로그래밍을 작성하기는 점점 쉬워지고 있지만, 안전성과 응답 가능 상태를 유지하게 작성하기는 여전히 어렵다. 병렬 스트림 파이프라인 프로그래밍에서도 다름 없다. 스트림 병렬화 스트림에서 parallel 사용시에 데이터 소스가 Stream.iterate거나 중간 연산으로 ..
자바7까지는 일련의 원소를 반환하는 메서드의 반환 타입으로 컬렉션 인터페이스나 Iterable, 배열을 써왔다. 그런데, 자바8의 스트림 도입 이후 이 선택이 복잡해졌다. Iterable vs Stream 스트림은 반복을 지원하지 않는다. (반환된 스트림을 for-each 반복 X) 사실 Stream 인터페이스는 Iterable 인터페이스가 정ㅇ의한 추상 메서드를 전부 포함하고, Iterable 인터페이스가 정의한 방식대로 동작 → 그런데도 for-each로 반복 못하는 이유? Stream이 Iterable을 확장(extends)하지 않아서. 스트림을 반복하기 위한 우회 Stream의 iterator 메서드에 메서드 참조를 건네면 스트림을 반복할 수 있을 것 같다. 그러나 다음 코드는 컴파일 오류가 난다..
스트림 패러다임 핵심은 계산을 일련의 변환으로 재구성하는 부분 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야함 순수 함수 : 오직 입력만이 결과에 영향을 주는 함수(다른 가변 상태 참조/변경 x) // 스트림 패러다임을 이해하지 못한 채 API만 사용한 코드 - 따라하지 말 것 Map freq = new HashMap(); try (Stream words = new Scanner(file).tokens()) { words.forEach(word -> { freq.merge(word.toLowerCase(), 1L, Long::sum); // 외부 상태 수정 }); } 위 코드는 스트림 API의 이점을 살리지 못한 스트림을 가장한 반복적 코드이다. 이 코드의 모든 작업은 종단연..
스트림 API 자바8부터는 스트림 API가 다량의 데이터 처리작업을 돕고자 추가되었다. 스트림(stream)은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻함 스트림 파이프라인은 이 원소들로 수행하는 연산단계를 뜻함 스트림 안의 데이터 원소들은 객체 참조나 기본 타입 값(int, long, double 지원) 스트림 파이프라인 소스 스트림에서 시작하여 종단 연산으로 끝남. 그 사이에 중간 연산이 있을 수 있음 중간 연산은 스트림을 어떤 방식으로 변환 한 스트림을 다른 스트림으로 변환 변환된 스트림의 원소 타입은 변환 전과 같을 수도 있고 다를 수도 있음 종단 연산은 마지막 중간 연산이 내놓은 스트림에 최후의 연산을 함 스트림은 지연 평가(lazy evaluation) 된다. 종단연산에 쓰이지 않는 데이터 원..
자바가 람다를 지원하면서 바뀐 것 상위 클래스의 기본 메서드를 재정의해 원하는 동작을 구현하는 템플릿 메서드 패턴 → 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 것 (함수 객체를 매개변수로 받는 생성자와 메서드를 더 많이 만들어야함 ex) LinkedHashMap 의 removeEldestEntry 메서드 (맵의 가장 오래된 원소 제거) //재정의함 -> 원소가 100개를 넘어가면 가장 오래된 원소를 제거 protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 100; } 위 코드를 람다를 사용하여 함수형 인터페이스를 만들 경우에는 아래와 같다. //불필요한 함수형 인터페이스 - 대신 표준 함수형 인터페이스를 사용하라 @Fu..
메서드 참조 람다가 익명클래스보다 간결하다. 그런데 자바에서 함수 객체를 람다보다 간결하게 만드는 방법이 메서드 참조이다. map.merge(key, 1, (count,incr) -> count+incr); 위의 람다를 사용한 코드는 count와 incr이 특별이 하는 일 없이 공간만 차지하게 된다. 그래서 람다 대신 이 메서드의 참조를 전달하면 더 보기 좋게 결과를 얻을 수 있다. map.merge(key, 1, Integer::sum); 람다로 할 수 없는 일이라면 메서드 참조로도 할 수 없다. 보통은 메서드 참조가 더 간결하지만, 상황마다 다르다. //메서드 참조 service.execute(GoshThisClassNameIsHumongous::action); // 람다를 사용하는 경우 -> 더 간..
예전 자바에서는 함수 객체를 만드는 주요 수단은 익명 클래스였다. 함수 객체 : 추상 메서드를 하나만 담은 인터페이스의 인스턴스 //익명 클래스의 인스턴스를 함수 객체로 사용 - 낡은 기법 Collections.sort(words, new Comparator() { @Override public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); 람다식 자바8부터 추상 메서드 하나만 담은 함수형 인터페이스의 인스턴스를 람다식을 이용해 만들 수 있게 되었다. 람다는 함수나 익명 클래스와 개념은 비슷 but 코드는 더 간결하고 명확함 //람다식을 함수 객체로 사용 - 익명 클래스 대체 Collec..
마커 인터페이스 자신을 구현하는 클래스가 특정 속성을 가짐을 나타내는 인터페이스 아무 메서드도 담고 있지 않음 예를 들어 Serializable , Cloneable 인터페이스 마커 인터페이스가 마커 애너테이션보다 좋은점 1) 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있으나 마커 애너테이션은 그렇지 않다. → 컴파일타임에 오류 검출 가능 Serializable의 writeObject() : 매개변수에서 Serializable 타입인지 확인을 안하고, Object 로 받아 instanceof로 타입검사를 하고 있다. → 직렬화할 수 없는 객체를 넘기면 런타임 오류로 발견할 수가 있다. → 마커 인터페이스의 장점인 컴파일 오류 검출을 살리지 못한 케이스이다. 2) 적용 대..
@Override 자바가 기본으로 제공하는 애너테이션 중 중요한 애너테이션 메서드 선언에만 달 수 있는 애너테이션 상위 타입의 메서드를 재정의했음을 뜻함 → 버그 예방 만약, equals 메서드를 다음과 같이 재정의하였다면? public boolean equals(Bigram bigram) { return bigram.first == this.first && bigram.second == this.second; } 위의 equals 메서드는 재정의(overriding)한게 아니라 다중정의(overloading) 해버렸다. Object의 equals 를 재정의하려면 매개변수 타입을 Object로 해야하는데 그렇게 하지 않았다. 실수로 별개인 equals를 새로 정의한 꼴이 되었다. 만약 그냥 위와 같이 코..
명명 패턴 전통적으로 도구나 프레임워크가 특별히 다뤄야할 프로그램 요소에는 딱 구분되는 명명패턴을 적용해왔다. ex) JUnit3의 테스트 메서드 이름을 test로 시작하게함 //JUnit3 : 메서드 이름을 test로 시작해야함 public class helloTest extends TestCase { public void testHello(){ String hello = "hello" } } //JUnit4 : @Test 애너테이션을 사용해 테스트 public class helloTest { @Test public void testHello(){ String hello = "hello" } } 명명 패턴의 단점 오타가 나면 안됨 올바른 프로그램 요소에서만 사용될 보증 없음 프로그램 요소를 매개변수로 ..