참고 강의 : 백기선님의 더 자바, Java8
Optional<T>
예시)
@FunctionalInterface
public interface RunSomething {
void doIt();
}
-> 추상 메서드를 꼭 한가지만 가지고 있어야함
public class Foo {
public static void main(String[] args) {
// 익명 내부 클래스 anonymous inner class
RunSomething runSomething = () -> System.out.println("hello");
}
}
Function<T, R>
BiFunction<T, U, R>
Consumer<T>
Supplier<T>
Predicate<T>
UnaryOperator<T>
BinaryOperator<T>
import java.util.function.Function;
public class Plus10 implements Function<Integer, Integer> {
@Override
public Integer apply(Integer integer) {
return integer + 10;
}
}
Function<T, R>첫번째 파라미터 입력값의 타입, 두번째 파라미터가 리턴값의 타입
public class Foo {
public static void main(String[] args) {
Plus10 plus10 = new Plus10();
System.out.println(plus10.apply(1));
}
}
결과 : 11
-> 클래스를 만들지 않고 람다를 활용해서 바로 구현 가능
public class Foo {
public static void main(String[] args) {
Function<Integer, Integer> plus10 = (i) -> i + 10;
//UnaryOperator<Integer> plus10 = (i) -> i + 10;
Function<Integer, Integer> multiply2 = (i) -> i * 2;
System.out.println(plus10.apply(1));
//plus10.compose(multiply2);
System.out.println(plus10.andThen(multiply2).apply(2)); //24
}
UnaryOperator<Integer>
로 대체 가능(인자 리스트) -> {바디}
public class Foo {
public static void main(String[] args) {
BiFunction<Integer> sum = (a, b) -> a + b;
}
}
public class Foo {
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
private void run() {
int baseNumber = 10;
//로컬 클래스
class LocalClass {
void printBaseNumber() {
int baseNumber = 11;
System.out.println(baseNumber); //11
}
}
//익명 클래스에서 로컬 변수 참조하기
Consumer<Integer> integerConsumer = new Consumer<Integer>() {
@Override
public void accept(Integer baseNumber) {
System.out.println(baseNumber); //파라미터
}
}
//람다에서 로컬 변수 참조
InConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
printInt.accept(10);
}
}
baseNumber 로컬 variable이 캡쳐가 됨
익명 클래스와 로컬 클래스는 클래스 안에서 각각의 scope을 가짐
람다는 다름.
InConsumer printInt = (baseNumber) -> {
System.out.println(i + baseNumber);
};
baseNumber라는 변수명 새로 정의 불가함. 같은 스코프이기 때문에
로컬 변수 캡처
effective final
익명 클래스 구현체와 달리 쉐도윙하지 않는다
람다가 하는 일이 기존 메소드 또는 생성자를 호출하는 거라면, 메소드 레퍼런스를 사용해서 간결하게 표현할 수 있다.
public class Greeting {
private String name;
public Greeting() {
}
public Greeting(String name) {
this.name = name;
}
public String hello(String name) {
return "hello " + name;
}
public static String hi(String name) {
return "hi " + name;
}
}
public class App {
public static void main(String[] args) {
//Greeting 클래스의 static 메서드 hi메서드 참조
UnaryOperator<String> hi = Greeting::hi;
//특정한 인스턴스의 메서드 사용할 땐
Greeting greeting = new Greeting();
UnaryOperator<String> hello = greeting::hello;
System.out.println(hello.apply("hi"));
//생성자 참조 -> 입력값 X
Supplier<Greeting> newGreeting = Greeting::new;
Greeting greeting = newGreeting.get();
//생성자 참조 -> 입력값 O
Function<String, Greeting> neewGreeting = Greeting::new;
Greeting test = neewGreeting.apply("test");
System.out.println(test.getName());
//임의 객체의 인스턴스 메소드 참조
String[] names = {"Btest", "Atest", "Ctest"};
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names)); // A, B, C
}
}
스태틱 메소드 참조 | 타입::스태틱 메소드 |
특정 객체의 인스턴스 메서드 참조 | 객체 레퍼런스::인스턴스 메소드 |
임의 객체의 인스턴스 메소드 참조 | 타입::인스턴스 메소드 |
생성자 참조 | 타입::new |
메소드 또는 생성자의 매개변수로 람다의 입력값을 받는다
리턴값 또는 생성한 객체는 람다의 리턴값이다
인터페이스에 메소드 선언이 아니라 구현체를 제공하느 ㄴ방법
해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.
기본 메소드는 구현체가 모르게 추가된 기능으로 그만큼 리스크가 있다.
Object가 제공하는 기능 (equals, hasCode)는 기본 메소드로 제공할 수 없다.
본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.
인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
인터페이스 구현체가 재정의 할 수도 있다.
public interface Foo {
void printName();
//void printNameUpperCase(); // 이걸 추가하면 모든 클래스가 컴파일 에러가 남. 추가한 추상화메서드를 구현하지 않았기 때문에 -> default로 해결
/**
* @implSpec
* 이 구현체는 getName()으로 가져온 문자열을 대문자로 바꿔 출력한다.
*/
default void printNameUpperCase() {
System.out.println(getName().toUpperCase());
}
static void printAnything() {
System.out.println("Foo");
}
String getName();
}
public class DefaultFoo implements Foo {
String name;
public DefaultFoo(String name) {
this.name = name;
}
@Override
public void printName() {
System.out.println(this.name);
}
@Override
public String getName() {
return this.name;
}
}
import java.util.Arrays;
public class App {
public static void main(String[] args) {
Foo foo = new DefaultFoo("yulhee");
foo.println(); // yulhee
foo.printNameUpperCase(); // YULHEE
Foo.printAnything();
}
}
public interface Bar extends Foo {
void printNameUpperCase();
}
-> Bar에서 Foo가 제공하는 기본 구현체를 제공하고 싶지 않을 때
추상 메서드로 선언하여 다시 재정의 해주면 됨
import java.util.*;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
List<String> name = new ArrayList<>();
name.add("yulhee");
name.add("duboo");
name.add("gab");
name.add("foo");
name.forEach(System.out::println);
for (String n: name) {
System.out.println(n);
}
Spliterator<String> spliterator = name.spliterator();
Spliterator<String> spliterator2 = spliterator.trySplit(); //절반으로 쪼개줌
while(spliterator.tryAdvance(System.out::println));
System.out.println("============");
while(spliterator2.tryAdvance(System.out::println));
name.stream().map(String::toUpperCase)
.filter(s -> s.startsWith("y"))
.collect(Collectors.toSet());
name.removeIf(s -> s.startsWith("y"));
Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
name.sort(compareToIgnoreCase.reversed()); //문자열 역순으로 정렬
name.sort()
}
}
-> spliterator 출력 결과
yulhee
duboo
============
gab
foo
public class App {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
name.add("yulhee");
name.add("duboo");
name.add("gab");
name.add("foo");
Stream<String> stringStream = names.stream().map(String::toUpperCase);
//종료형 오퍼레이터 선언 -> collect
//collect 사용해야만 println문 실행됨
List<String> collect = names.stream().map((s) -> {
System.out.println(s); //소문자 출력
return s.toUpperCase();
}).collect(Collectors.toList());
collect.forEach(System.out::println); //대문자 출력
System.out.println("============");
names.forEach(System.out::println); //소문자로 출력됨
}
public class OnlineClass {
private Integer id;
private String title;
private boolean closed;
public OnlineClass(Integer id, String title, boolean closed) {
this.id = id;
this.title = title;
this.closed = closed;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isClosed() {
return closed;
}
public void setClosed(boolean closed) {
this.closed = closed;
}
}
import java.util.ArrayList;
import java.util.List;
public class App {
public static void main(String[] args) {
List<OnlineClass> springClasses = new ArrayList<>();
springClasses.add(new OnlineClass(1, "spring boot", true));
springClasses.add(new OnlineClass(2, "spring data jpa", true));
springClasses.add(new OnlineClass(3, "spring mvc", false));
springClasses.add(new OnlineClass(4, "spring core", false));
springClasses.add(new OnlineClass(5, "rest api development", false));
System.out.println("spring 으로 시작하는 수업");
// TODO
springClasses.stream()
.filter(oc -> oc.getTitle().startsWith("spring"))
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("close 되지 않은 수업");
// TODO
springClasses.stream()
//.filter(oc -> !oc.isClosed())
.filter(Predicate.not(OnlineClass::isClosed))
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("수업 이름만 모아서 스트림 만들기");
// TODO
springClasses.stream()
.map(oc -> oc.getTitle())
.forEach(s -> System.out.println(s));
List<OnlineClass> javaClasses = new ArrayList<>();
javaClasses.add(new OnlineClass(6, "The Java, Test", true));
javaClasses.add(new OnlineClass(7, "The Java, Code manipulation", true));
javaClasses.add(new OnlineClass(8, "The Java, 8 to 11", false));
List<List<OnlineClass>> keesunEvents = new ArrayList<>();
keesunEvents.add(springClasses);
keesunEvents.add(javaClasses);
System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
// TODO
//keesunEvents.stream().flatMap(list -> list.stream())
keesunEvents.stream()
.flatMap(Collection::stream)
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");
// TODO
Stream.iterate(10, i -> i + 1)
.skip(10)
.limit(10)
.forEach(System.out::println);
System.out.println("자바 수업 중에 Test가 들어있는 수업이 있는지 확인");
// TODO
boolean test = javaClasses.stream().anyMatch(oc -> oc.getTitle().contains("Test"));
System.out.println(test);
System.out.println("스프링 수업 중에 제목에 spring이 들어간 것만 모아서 List로 만들기");
// TODO
List<String> spring = springClasses.stream()
.filter(oc -> oc.getTitle().contains("spring"))
.map(OnlineClass::getTitle)
.collect(Collectors.toList());
spring.forEach(System.out::println);
}
}