2020-10-13 백기선님 더자바 강의정리
더자바
함수형 인터페이스와 람다
-
인터페이스에 추상메서드가 1개만 있으면 함수형 인터페이스.
-
2개가 있으면 안된다.
-
@FunctionalInterface public interface RunSomething { void doIt(); static void printName() { System.out.println("jimmy"); } default void printAge() { System.out.println("30"); } } public class Run { public static void main(String[] args) { // 익명 내부 클래스 RunSomething runSomething = new RunSomething() { @Override public void doIt() { System.out.println("구현체"); } }; // 람다표현식 RunSomething runSomething = () -> System.out.println("구현체"); } RunSomething runSomething = () -> { System.out.println("구현체"); System.out.println("여러줄"); }; runSomething.doIt(); }
- 함수형 인터페이스를 잘 쓰기 위해서 @FuntionalInterface를 붙이면 좋다.
-
함수형 인터페이스
- 추상메서드를 1개만 가지고 있는 인터페이스
- SAM(Single Abstract Method) 인터페이스
- @FuntionalInterface 어노테이션을 갖고 있는 인터페이스
-
람다 표현식
- 함수형 인터페이스의 인스턴스를 만드는 방법으로 쓸 수 있다.
- 코드를 줄일 수 있다.
- 메서드의 매개변수, 리턴 타입, 변수로 만들어 사용할 수 있다.
자바에서 함수형 프로그래밍
- 함수를 First Class Citizen으로 사용할 수 있다.
- 순수함수 : 함수내부에서만 선언된 변수만 써야한다.
- 사이드 이팩트를 만들 수 없다. => 함수밖에 있는 값 변경을 못한다.
- 상태가 없다. => 함수 밖에 정의되는.
- 고차함수(High-Order Function) : 함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수 있다.
- 불변성
함수형 메서드
- Function<T, R> : 정의하면 apply로 적용
- UnaryOperator<T> : 입력값의 타입과 결과값의 타입이 같을때
public class Run {
public static void main(String[] args) {
Function<Integer, Integer> plus10 = (i) -> i+10;
Function<Integer, Integer> multiply2 = (i) -> i * 2;
Function<Integer, Integer> multiply2AndPlus10 = plus10.compose(multiply2);
System.out.println(multiply2AndPlus10.apply(2));
Function<Integer, Integer> tmp = plus10.andThen(multiply2);
System.out.println(tmp.apply(2));
}
}
// result
14
24
-
compose가 입력값을 넣기전에 연산을 한번 한다. 고차함수의 성격
-
compose안에 있는 연산을 먼저하고 그다음 연산을 수행
-
andThen()은 앞에 연산을 먼저하고 안에 연산을 수행.
-
BiFunction<T, U, R> : 입력값을 2개 받는다.
-
Consum<T> : 입력만 받고 리턴은 void
-
Supplier<T> : 입력값이 없고 어떤 값을 받아올지 타입을 정한다
-
Supplier<Integer> get10 = () -> 10;
-
-
Predicate<T> : 인자값을 1개받아서 Boolean으로 리턴해준다.
-
public class Run { public static void main(String[] args) { Predicate<String> startWith = (str) -> str.startsWith("h"); Predicate<Integer> isEven = (i) -> i % 2 == 0; System.out.println(startWith.test("hello")); System.out.println(isEven.test(10)); } }
-
람다표현식
-
body가 1줄이면 {}생략 가능
-
변수캡처
-
package me.jimmy.java8; import java.util.function.Consumer; import java.util.function.IntConsumer; public class Run { public static void main(String[] args) { Run run = new Run(); run.tmp(); } private void tmp() { int baseNumber = 10; // 로컬클래스 class LocalClass { void printBaseNumber() { int baseNumber = 11; // 함수안에 있는 baseNumber가 tmp에 있는 baseNumber를 가린다. System.out.println("local class " + baseNumber); // 11 } } // 익명클래스 Consumer<Integer> integerConsumer = new Consumer<Integer>() { @Override public void accept(Integer baseNumber) { System.out.println("익명 클래스 " + baseNumber); // parameter 이름으로 전달된 baseNumber를 쓴다. } }; IntConsumer printInt = (i) -> { System.out.println(i + baseNumber); }; printInt.accept(10); } // 에러난다. IntConsumer printInt = (baseNumber) -> { System.out.println(baseNumber); }; }
-
로컬클래스와 익명클래스에서 외부변수를 참조하는게 람다와는 다르다.
-
baseNumber가 사실상 final. 어디서도 변경하지 않는 경우. 이를 사실상 final. effective final. 이 경우 로컬클래스, 익명클래스, 람다에서 모두 다 baseNumber를 참조 가능하다.
-
람다와 익명클래스,로컬클래스에서 스코프영역이 적용되는게 다르다. 람다는 스코프가 람다를 감싸고 있는 것과 같다. 따라서 쉐도잉이 일어나지 않는다.
- 같은 스코프에서는 똑같은 이름의 변수를 정의할 수 없다.
-
메서드레퍼런스
-
::가 붙으면 메서드 레퍼런스다.
-
public class GreetingApp { public static void main(String[] args) { Greeting greeting = new Greeting(); UnaryOperator<String> hello = greeting::hello; // non-static UnaryOperator<String> hi = Greeting::hi; // static Supplier<Greeting> newGreet = Greeting::new; newGreet.get(); Function<String, Greeting> conGreet = Greeting::new; conGreet.apply("jim"); } } 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; } }
-
메서드 참조방법
- 스태틱 메서드 참조 : 타입::스태틱 메서드
- 특정 객체의 인스턴스 메서드 참조 => 객체레퍼런스::인스턴스메서드
- 임의 객체의 인스턴스 메서드 참조 => 타입::인스턴스 메서드
- 생성자 참조 => 참조::new
- 메서드, 생성자의 매개변수로 람다의 입력값을 받고. 리턴값, 생성한 객체는 람다의 리턴값이다.
public class GreetingApp {
public static void main(String[] args) {
String [] names = {"jimmy", "a", "b"};
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return 0;
}
});
Arrays.sort(names, String::compareToIgnoreCase);
Arrays.sort(names, (o1, o2) -> {
return 0;
});
}
}
Written on October 13, 2020