JAVA - Java 8 정리

우야·2021년 8월 31일
0

Interface 기본 메소드

  1. default
    인터페이스에 비추상 메서드인 기본 메서드 구현을 추가할 수 있음
interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

Lamda 표현식

람다 : 이름 없는 함수, 익명 함수 (Anonymous Function)
람다의 특징은 메서드의 인수로 전달될 수 있고, 변수로 저장될 수 있음
표현 방법 :

(parameters) -> expression

(parameters) -> { statements; }
  1. java8 이전과 java8 람다식 비교
#이전 방법
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

#lamda식 방법
# 1.
Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});
# 2.
Collections.sort(names, (String a, String b) -> b.compareTo(a));
# 3.
Collections.sort(names, (a, b) -> b.compareTo(a));

Lamda 선언 방법 : 함수형 Interface 구조

  • Lamda는 Interface 유형으로 정확히 하나의 추상 메소드 선언을 포함해야 한다.
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
# or
Converter<String, Integer> converter = Integer::valueOf; # method reference

Integer converted = converter.convert("123");
System.out.println(converted);    // 123
#참고 : method reference는 함수를 실행하는것이 아니기때문에 ()를 붙이지 않음
이 부분은 아래에 따로 다루기로 해야 할 것 같다!!
  • 람다 Scope
    -- 람다 식에서 외부 범위 변수에 액세스하는것은 익명 개체와 매우 유사함
    -- 인스턴트 필드 및 정적 변수뿐만 아니라 로컬 외부 범위에서 최종 변수에 엑세스할 수 있음

-- 지역변수 접근 : 최종 지역변수를 읽을 수 있음

    - 람다식에서 num을 사용할수 있음
int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3


    - 컴파일 에러 : 람다식에서도 num을 사용하지 못하고, 이후에도 지역변수로 사용하지 못함
int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;

-- Interface 기본 메소드 접근 안됨 (제일 위의 예제)

# 컴파일 에러 : 람다식에서는 동작하지 않음
Formula formula = (a) -> sqrt( a * 100);

Java 8에 내장된 기능 Interface

  • @FunctionalInterface주석을 통해 Lamda 지원을 활성화하도록 확장됨
  1. Predicates
  • Boolean값 함수로 논리 용어 (and, or, not)로 구성하기 위한 기본 메소드가 포함
Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
  1. Function
  • 하나의 인수를 받아들이고 결과를 생성
  • 여러 함수를 연결할 수 있음 (compose, andThen)
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"
  1. Supplier
  • 지정된 제네릭 형식의 결과를 생성
  • 함수와 다르게 인수를 허용하지 않음 (기본 생성자에 사용 가능)
Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person
  1. Consumer
  • 단일 입력 인수에 대해 수행할 작업
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
  1. Comparator
  • 이전 Java에 알려진것이나, 다양한 기본 메소드가 추가됨
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0
  1. Optional
  • 기능 Interface는 아니다.
  • null, non-null의 값을 위한 간단한 container이고,
  • NullPointException을 방지하기 위한 유틸이다.
Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"
  1. stream
    Stream 페이지에서 보도록한다.
  2. filter
    Stream 페이지에서 보도록한다.
  3. sorted
    Stream 페이지에서 보도록한다.
  4. map (Map Collection 아님)
    Stream 페이지에서 보도록한다.
  5. match (anymatch, allMatch, noneMatch)
    Stream 페이지에서 보도록한다.
  6. count
    Stream 페이지에서 보도록한다.
  7. reduce
    Stream 페이지에서 보도록한다.
  8. parallelStream
    Stream 페이지에서 보도록한다.
  9. Map (Map Collection)
  • putIfAbsent : map에 key가 없으면 데이터 셋을 넣고 key가 있으면 건너뜀
  • computeIfPresent : map의 데이터를 계산할 수 있음
  • remove : key, value가 같을때 삭제
  • getOrDefault : 없으면 default 값 return
  • merge : 값이 없는경우 삽입하고 있으면 병합
Map<Integer, String> map = new HashMap<>();

### ex) putIfAbsent
for (int i = 0; i < 10; i++) {
    map.putIfAbsent(i, "val" + i);
}

map.forEach((id, val) -> System.out.println(val));

### ex) computeIfPresent
map.computeIfPresent(3, (num, val) -> val + num);
map.get(3);             // val33


### ex) remove 
map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null

### ex) getOrDefault
map.getOrDefault(42, "not found");  // not found

### ex) merge
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat
  1. Date API
  • 완전히 새로운 날짜 및 시간 API가 포함
  • Date API는 Joda-Time 라이브러리 와 비슷 하지만 동일하지는 않음
  • Clock
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();

Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // legacy java.util.Date
  • ZoneId
    • 인스턴스와 현지 날짜, 시간 사이를 변환하는 Offset을 정의
System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]
  • LocalTime
    • ZoneId가 없는 시간을 나타냄
    • 시간 문자열의 구문 분석 및 새로운 인스턴스 생성이 가능한 팩토리 메서드 제공
LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

System.out.println(hoursBetween);       // -3
System.out.println(minutesBetween);     // -239

####

LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT)
        .withLocale(Locale.GERMAN);

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
System.out.println(leetTime);   // 13:37
  • LocalDate
    • 고유한 날짜
    • 일, 월, 연도를 더하거나 빼서 새 날짜를 계산하는 방법을 보여줌
    • 날짜 문자열의 구문 분석 및 새로운 인스턴스 생성이 가능한 팩토리 메서드 제공
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);    // FRIDAY

###
DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedDate(FormatStyle.MEDIUM)
        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
System.out.println(xmas);   // 2014-12-24
  • LocalDateTime
    - 날짜-시간
    - 시간대의 추가 정볼르 사용하여 인스턴스로 변환가능
    - 사용자 지정 포맷터를 만들수 있음
LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);      // WEDNESDAY

Month month = sylvester.getMonth();
System.out.println(month);          // DECEMBER

long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);    // 1439

### zone 처리
Instant instant = sylvester
        .atZone(ZoneId.systemDefault())
        .toInstant();

Date legacyDate = Date.from(instant);
System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

### formatter 처리
DateTimeFormatter formatter =
    DateTimeFormatter
        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string);     // Nov 03, 2014 - 07:13
  1. Annotations
  • wrapper annoation 제공
@interface Hints {
    Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
    String value();
}

@Hint("hint1")
@Hint("hint2")
class Person {}

Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);          // 2

Method Reference

  • Method reference란?
    • 기존 람다식을 더 줄일 수 있는 표현법으로 메소드, 생성자의 참조를 전달할 수 있음
    • "::"로 표현됨
  • 기본 표현식
    • ClassName::Method
    • ex) Person::getAge
1. (Person p) -> p.getAge()                     ==>    Person::getAge

2. () -> Thread.currentThread().dumpStack()  ==>   Thread.currentThread::dumpStack

3. (str, i) -> str.substring(i)                      ==>    String::substring

4. (String s) -> System.out.println(s)           ==>    System.out::println
  • 만드는 방법
  1. class 내부static 함수
(args) -> ClassName.staticMethod(args)
ClassName::statckMethod

---

ex) 
(String s) -> Integer.parseInt(s); /*Lambda expression*/
Integer::parseInt; //method reference
  1. class 내부 일반 함수
(arg0, rest) -> arg0.instanceMethod(rest);
arg0의ClassName::instanceMethod;

---

ex)
(List<String> list, element) -> list.contains(element); /*Lambda expression*/
List::contains; //method reference
  1. 지역변수에 할당된 object
(args) -> expr.instnaceMethod(args)
expr::InstanceMethod

---

class Person {
    String firstName;
    String lastName;
    
    Person() {}
    
    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getFristName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        firstName = firstName;
    }
}

public class Main {
    public Person mPerson = new Person();
    public void methodReferenceTest() {
        Consumer<String> setName1 = name -> mPerson.setFirstName(name);
        Consumer<String> setName2 = mPerson::setFirstName; /*method reference*/

		Supplier<String> getName1 = () -> mPerson.getFristName();
		Supplier<String> getName2 = mPerson::getFristName; /*method reference*/
    }
}
  1. 생성자 레퍼런스
  • 생성자 reference를 사용할 경우 객체의 생성이 delay됨 (lazy initialize)
  • factory pattern을 사용할때 유용하게 사용할 수 있음
Supplier<Person> constructor1 = Person::new;
Person p1 = constructor1.get(); ## 생성자로 객체 생성됨

---

interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

# 생성자에 String 인수 하나가 있을때
PersonFactory<Person> personFactory = Person::new;
Person p2 = personFactory.create("Peter", "Parker"); ## 생성자로 객체 생성됨
profile
Fullstack developer

0개의 댓글