제네릭 public interface Comparable { int compareTo(T o); } 위 Comparable의 메소드는 Comparable, Comparable 등을 구현할 수 있다. 타입을 명시하여 String, Integer 각 타입별 중복된 메소드를 생성할 필요없이 제네릭을 사용하여 중복된 코드를 공통으로 묶을 수 있고, 타입에 자유로워질 수 있다. 형변환의 에러는 컴파일시에 나타나지 않고 런타임 오류로 발생하는데, 이 에러를 컴파일시에 발견할 수 있다는 장점도 제네릭의 큰 장점이다. 제네릭은 static 변수에 사용이 불가능하다. static 변수는 클래스 변수로, 모든 인스턴스에게 공유되는 변수이다. static 변수에 제네릭을 사용하게되면 모든 인스턴스에게 공유되는 변수가 경우에..
숫자형 스트림 reduce 메서드를 사용하여 스트림 요소의 합을 구하는 예제를 보자. int calories = menu.stream() .map(Dish::getCalories) .reduce(0, Integer::sum); 위 코드에는 박싱이 숨어져있는데, 내부적으로 합계를 계산하기 전에 Integer를 기본형(int)로 언박싱 해야한다. int calories = menu.stream() .map(Dish::getCalories) .sum(); // 호출 불가능 직접 sum 메서드를 호출한다면 훨씬 좋겠지만, 위 코드처럼 sum 메서드를 직접 호출할 수 없다. map 메서드가 Stream를 생성하기 때문이다. 이를 해결하기 위해서 스트림은 API 숫자 스트림을 효율적으로 처리할 수 있도록 기본형 특..
싱글턴 싱글턴(Singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스이다. 무상태(stateless) 객체나 설계상 유일해야하는 시스템 컴포넌트이다. 싱글턴을 만드는 방식은 2가지가 있다. 첫번째는 public static final 필드 방식이고, 두번째는 정적 팩터리 메서드를 public static 멤버로 제공하는 방법이다. 2가지 방식의 공통점은 생성자는 private로 감춰두고, 유일한 인스턴스에 접근할 수 있는 수단으로 public static 멤버를 하나 마련해두는 것이다. public static final 필드 방식 public class Test { public static final Test test = new Test(); private Test() { ... } //..
스트림 연산 java.util.stream.Stream 인터페이스는 많은 연산을 정의한다. 스트림 인터페이스의 연산은 2가지로 구분된다. List names = menu.stream() // 요리 리스트에서 스트림 얻기 .filter(dish -> dish.getCalories() > 300) // 중간연산 .map(Dish::getName) // 중간연산 .limit(3) // 중간연산 (쇼트서킷) .collect(toList()); // 스트림을 리스트로 변환 1) 중간 연산 : 연결할 수 있는 스트림 연산 (filter, map, limit) 2) 최종 연산 : 스트림을 닫는 연산 (collect) 중간 연산 filter나 sorted 같은 중간 연산은 다른 스트림을 반환한다. 중간 연산은 중간 연..
스트림과 컬렉션 자바의 기존 컬렉션과 새롭게 추가된 스트림 모두 연속된 요소 형식의 값을 저장하는 자료구조의 인터페이스를 제공한다. 연속된이란, 순서와 상관없이 아무 값에나 접속하는 것이 아닌 순차적으로 접근한다는 것을 의미한다. 스트림과 컬렉션의 차이는 데이터를 언제 계산하느냐이다. 1) 컬렉션 컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조다. 따라서 컬렉션의 모든 요소는 컬렉션에 추가하기 전에 계산되어야한다. 2) 스트림 스트림은 요청할 때만 요소를 계산하는 고정된 자료구조이다. 스트림에 요소를 추가하거나 스트림에서 요소를 제거할 수 없다. 사용자가 요청한 값만 스트림에서 추출한다. 스트림은 생산자(producer)-소비자(consumer) 관계를 형성한다. 사용자가 데이터를..
스트림 정의 스트림이란 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소라고 할수있다. 1) 연속된 요소 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공한다. 요소 저장 및 접근 연산이 주를 이루는 컬렉션과 다르게, 스트림은 filter, sorted, map 처럼 표현 계산식이 주를 이룬다. 컬렉션의 주제는 데이터이고 스트림의 주제는 계산이다. 2) 소스 스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비한다. 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지되는 것처럼, 리스트로 스트림을 만들면 스트림의 요소는 리스트의 요소와 같은 순서를 유지한다. 3) 데이터 처리 연산 스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베..
도입 모든 자바 애플리케이션은 컬렉션을 만들고 처리하는 과정을 포함한다. 컬렉션은 대부분의 프로그래밍 작업에 사용될 정도로 어디서든 사용되어지고있다. 하지만 컬렉션을 많이 사용함에도 불구하고 완벽한 컬렉션 연산을 지원하기에는 아직까지도 부족하다. 많은 요소를 포함하는 컬렉션은 어떻게 처리해야할까? 성능을 높이려면 멀티코어 아키텍처를 활용하여 병렬로 컬렉션의 요소를 처리해야한다. 하지만 병렬 처리 코드를 구현하는 것은 단순 반복 처리 코드에 비해 복잡하고 어렵다. 이러한 복잡한 코드는 디버깅도 어렵다. 프로그래머가 귀중한 시간을 절약하고, 편리한 삶을 누릴 수 있도로 자바 언어 설계자들이 내린 결정이 바로 스트림이다. 스트림이란? 스트림은 자바 8 API에 새로 추가된 기능이다. 스트림은 데이터 컬렉션 반..
메서드 참조 메서드 참조를 이용하면 기존의 메서드 정의를 재활용해서 람다처럼 전달할 수 있다. inventory.sort(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); 위 코드를 메서드 참조와 java.util.Comparator.comparing을 활용한 코드로 바꿔보자. // 메서드 참조의 사용 // Apple 클래스에 정의된 getWeight의 메서드 참조 // 실제로 메서드를 호출하는 것이 아니므로 괄호 생략 // 결과적으로 (Apple a) -> a.getWeight()를 축약한 것 inventory.sort(comparing(Apple::getWeight)); // = (Apple a) -> a.getWeight() 위 생..
도입 람다로 함수형 인터페이스의 인스턴스를 만들 수 있다. 하지만 람다 표현식 자체에는 어떤 함수형 인터페이스를 구현하는지의 정보가 포함되어있지 않으므로 람다의 실제 형식을 제대로 파악해야한다. 형식 검사 람다가 사용되는 context를 이용해서 람다의 형식을 추론할 수 있다. 어떤 context에서 기대되는 람다 표현식의 형식을 대상 형식이라고 부른다. 예를 들면 람다가 전달된 메서드 파라미터나 람다가 할당되는 변수에서 추론하는 것이다. List heightThan160cm = filter(inventory, (Height height) -> height.getHeight() > 160); 위 예제는 아래 순서로 형식 확인 과정을 거친다. (1) filter 메서드의 선언을 확인한다. (2) filte..
함수형 인터페이스의 사용 함수형 인터페이스는 오직 1개의 추상 메서드를 지정하고, 이 추상 메서드는 람다 표현식의 시그니처를 묘사한다. 함수형 인터페이스의 추상 메서드의 시그니처를 함수 디스크립터라고 한다. 다양한 람다 표현식을 사용하려면 공통의 함수 디스크립터를 기술하는 함수형 인터페이스 집합이 필요하다. 자바 API에 Comparable, Runnable, Callable 등의 다양한 함수형 인터페이스가 있지만 자바 8에 새로 추가된 함수형 인터페이스에 대해 알아보자. Predicate java.util.function.Predicate 인터페이스는 test라는 추상 메서드를 정의하며 test는 제네릭 형식 T의 객체를 인수로 받아 boolean 타입을 반환한다. 따로 정의할 필요 없이 바로 사용이 ..
생성자의 단점 생성자에는 제약이 하나 있는데, 선택적 매개변수가 많을 경우에 대응이 어렵다. 예를들어, 받아오는 매개변수에 따라 계속해서 생성되는 생성자의 코드를 보았을때 매개변수의 개수에 따라 호출되는 생성자를 짐작하기가 매우 혼잡해진다. 또는 생성자 호출을 위해서 설정하길 원하지않는 매개변수의 값까지 지정해줘야하는 불편함이 있다. 한 두개 정도는 괜찮을 수 있겠지만, 매개변수의 수가 늘어나게되면 걷잡을 수 없을정도가 된다. 코드를 읽을 때 각 값의 의미가 무엇인지 헷갈린다. 매개변수가 몇개인지 세어보며 항상 확인해야한다. 타입이 같은 매개변수가 연속으로 있으면 버그 발생 가능성이 높아진다. 실수로 매개변수의 순서가 바뀌더라도 컴파일러가 해당 에러를 잡지 못하여 런타임 에러로 이어지게된다. 자바빈즈 패..
함수 디스크립터 함수형 인터페이스의 추상 메서드 시그니처 = 람다 표현식의 시그니처 이다. 람다 표현식의 시그니처를 서술하는 메서드를 함수 디스크립터라고 부른다. 여기서 메서드 시그니처란, 메서드명/파라미터 순서/파라미터 타입/파라미터 개수를 의미한다. 왜 함수형 인터페이스를 인수로 받는 메서드에만 람다 표현식을 사용할 수 있을까? 언어 설계자들은 언어를 더 복잡하게 만들지 않는 방법을 선택했다. 대부분의 자바 프로그래머가 하나의 추상 메서드를 갖는 인터페이스에 이미 익숙하다. @FunctionalInterface 함수형 인터페이스에 @FunctionalInterface 어노테이션이 추가되었다. 해당 어노테이션을 선언하면 인터페이스가 함수형 인터페이스가 아니라면 컴파일 에러가 발생한다. 함수형 인터페이스..