들어가며
JAVA 12 가 나오는 마당에 아직도 어쩔수 없이(?) JAVA 8을 사용하지 못하는 사람들을 위해 JAVA 8 의 기본 사항만 짧게 정리해 봤다.
이런 강좌(?) 같은 글을 처음 써 보기 때문에 확신할 수 는 없지만, 대략, 자바 초보부터 전문가 까지 두루두루 읽고 이해할 수 있게 작성하는 것이 목표다.
새로운 기능
JAVA 8 은 JAVA 의 중요한 릴리즈 버전 중 하나를 말한다. 2014년에 최초 릴리즈 되었으며, 다음의 기능을 지원한다.
Lambda expression - 자바에서 Functional 프로그래밍이 일정 부분 가능해졌다.
Method reference - 함수를 파라메터로 사용할 수 있다.
Default method - 인터페이스에서 기본 메소드를 구현할 수 있다.
New tools - 새로운 컴파일러 도구와 유틸리티가 추가 되었다. (이건 잘 모름)
Stream API - 자바에서 파이프라인 처리가 가능하다.
Date Time API - 드디어… 날짜 시간 함수가 쓰레드에서 안전해졌다.
Optional - null 값 처리에 대한 자바 나름대로의 표준을 제공한다.
Nashorn, Javascript Engine - 자바스크립트 코드를 자바에서 실행 한다. (이건 잘 모름)
설치
이건… 알아서 하자…
Lambda expression
Syntax
람다의 표현식은 다음과 같다.
parameter -> expression body
특징
람다는 파라메터의 타입을 선언할 필요가 없다. 컴파일러가 파라메터의 값을 보고 판단한다.
파라메터가 한개인 경우는 괄호로 감싸지 않아도 된다. 단, 두개 이상인 경우라면 괄호로 감싸야 한다.
expression body 가 단일 명령어 행이면, 중괄호({})를 생략해도 된다. 단일 명령어 행이 아니면, 중괄호를 꼭 써야 한다.
중괄호의 존재 여부에 따라 return 구문을 생략할 수 있다.
// 타입 선언
(int a, int b) -> a + b;
// 타입 선언 생략
(a, b) -> a + b;
// return 구문사용 (중괄호 필요)
(a, b) -> { return a * b; };
정리하면
Method reference
Syntax
메소드 참조의 표현식은 다음과 같다.
Class::new
메소드 참조는 다음의 경우에 사용한다.
정적 메소드 호출
인스턴스 메소드 호출
new 연산자를 사용한 객체 생성
Sample Code
List names = new ArrayList();
names.add(“지우”);
names.add(“호준”);
names.add(“연석”);
names.add(“세종”);
names.forEach(System.out::println);
Default method
Java 8은 인터페이스에서 기본 메소드라는 새로운 개념을 도입했다.
Syntax
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
Multiple Default
인터페이스의 기본 메소드를 사용하면, 두개의 인터페이스를 구현하는 클래스에서 어떠한 기본 메소드를 사용해야 할지 정해야 한다.
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
public interface fourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
public class car implements vehicle, fourWheeler {
}
첫번째 방법으로는 기본 메소드를 재 정의 한다.
public class car implements vehicle, fourWheeler {
public void print() {
System.out.println(“I am a four wheel vehicle”);
}
}
두번째 방법으로는 super 연산자를 사용하여 지정한다.
public class car implements vehicle, fourWheeler {
default void print() {
vehicle.super.print();
}
}
정적 기본 메소드
Java 8 의 인터페이스는 정적 기본 메소드도 가능하다.
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
Stream API
Stream 은 Java 8 에서 새로 도입된 추상 레이어다. Stream 을 사용하면, SQL 과 비슷한 선언적 방식으로 데이터를 처리 할 수 있다.
Java 8은 두가지의 Stream 을 생성하는 메소드를 가지고 있다.
stream - 순차 처리
parallelStream - 병렬 처리
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
forEach
Stream 은 forEach 메소드를 제공하여 각 요소를 반복 한다. 아래 코드는 forEach 를 사용하여 10개의 난수를 출력하는 방법을 보여준다.
Random random = new Random();
random.ints()
.limit(10)
.forEach(System.out::println);
Map
map 은 각 요소를 결과에 맵핑하는데 사용한다. 아래 코드는 map 을 사용하여 고유한 정사각형을 출력한다.
List<Integer> numbers = Arrays.asList(1,2,3,4,3,5,2,1,2,3,5,3);
List<Integer> squaresList = numbers.stream()
.map( i -> i * i )
.distinct()
.collect(Collectors.toList());
Filter
filter 는 기준에 맞지 않는 요소를 제거하는데 사용한다.
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.stream()
.filter(string -> string.isEmpty())
.count();
Limit
limit 은 스트림의 크기를 줄이기 위해 사용한다.
Random random = new Random();
random.ints()
.limit(10)
.forEach(System.out::println);
Sorted
sorted 는 스트림을 정렬하는데 사용한다.
Random random = new Random();
random.ints()
.limit(10)
.sorted()
.forEach(System.out::println);
ParallelStream
parallelStream 은 병렬처리를 위해 사용한다.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.parallelStream()
.filter(string -> string.isEmpty())
.count();
Collect
스트림의 처리 결과를 결합하는데 사용한다.
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
String mergedString = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
Statistics
Java 8 에서는 스트림 처리가 완료될 때 모든 통계를 계산하기 위한 통계 처리기가 도입되었다.
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
Date Time API
Java 8 에서는 새로운 Date, Time API 가 도입되어 아래와 같은 기존의 Date, Time API 의 단점을 보완한다.
쓰레드로 부터 안전하지 않음. - java.util.Date 는 쓰레드로 부터 안전하지 않아, 개발자가 동시성 문제를 해결해야 했다.
이상한 디자인 - java.util.Date 는 1900 년 부터 시작하고, 월은 1에서 시작하지만, 날짜는 0에서 시작하는 등 일관성이 없었다. 또한 날짜 조작에 대한 메소드가 적었다.
어려운 시간대 처리 - 시간대 문제를 해결하기 위해 개발자가 많은 노력을 기울여야 했다.
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
public class Java8Test {
public static void main(String args[]) {
Java8Test java8test = new Java8Test();
java8test.testLocalDateTime();
}
public void testLocalDateTime() {
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("Current DateTime: " + currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: " + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();
System.out.println("Month: " + month +"day: " + day +"seconds: " + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2);
LocalDate date3 = LocalDate.of(2019, Month.DECEMBER, 12);
System.out.println("date3: " + date3);
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4);
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
Optional
optional 은 null 이 아닌 객체를 포함하는데 사용하는 컨테이너 객체다.
Guava 의 Optional 과 유사하다.