검색결과 리스트
로컬클래스에 해당되는 글 1건
- 2021.05.12 java 8 정리1
글
java 8 정리1
인프런 강의 4일차.
- 더 자바, Java 8 (백기선 강사님)
1. 함수형 인터페이스
- 추상 메소드를 딱 하나만 가지고 있는 인터페이스
- SAM (Single Abstract Method) 인터페이스
- @FunctionInterface 애노테이션을 가지고 있는 인터페이스
2. 람다 표현식
- 함수형 인터페이스의 인스턴스를 만드는 방법으로 쓰일 수 있다.
- 코드를 줄일 수 있다.
- 메소드 매개변수, 리턴타입, 변수로 만들어 사용할 수도 있다.
3. 자바에서 함수형 프로그래밍
- 함수를 First Class Object로 사용할 수 있다.
- 순수 함수(Pure Function)
-> 사이드 이펙트 만들 수 없다. (함수 밖에 있는 값을 변경하지 못한다)
-> 상태가 없다. (함수 밖에 정의되어 있는)
- 고차 함수(High-Order Function)
-> 함수가 함수를 매개변수로 받을 수 있고, 함수를 리턴할 수도 있다.
- 불변성
4. 단축키
-> Alt + Enter : 익명 내부 클래스를 람다로 바꿔주는 단축키(IntelliJ)
package me.whiteship.java8to11;
@FunctionalInterface
public interface RunSomething {
//추상 메소드가 1개만 있다면 함수형 인터페이스
//다른 구현된 메소드가 여러개이더라도 추상화 메소드가 1개라면 함수형 인터페이스
// @FuncationalInterface 추가 시 함수형 인터페이스로 취급되어 추상 메소드 1개만 선언이 가능하다.
//void doIt();
int doIt(int number);
//static method 정의 가능
static void printName(){
System.out.println("dhpark");
}
//default method 정의 가능
default void printAge(){
System.out.println("32");
}
}
package me.whiteship.java8to11;
public class Foo {
public static void main(String[] args) {
//익명 내부 클래스 anonymous inner class
/*
RunSomething runSomething = new RunSomething() {
@Override
public void doIt() {
System.out.println("Hello");
}
};
//Alt + Enter : 익명 내부 클래스를 람다 표현식으로 바꿔주는 자동완성 기능
RunSomething runSomething = () -> System.out.println("Hello");
runSomething.doIt();
*/
/*
함수형 인터페이스를 인라인으로 구현한 오브젝트라고 볼 수 있다.
이러한 형태는 java가 객체지향 언어이기 때문에 아래 스텝으로 처리된다.
1. System.out.println("Hello");를 runSomething 객체에 할당
2. runSomething 객체를 메소드 파라미터에 전달
3. doIt() 메소드 실행
위와 같은 호출을 First Class Object로 사용한 것으로 본다.
*/
/*
순수 함수 : 입력받은 값이 동일한 경우 결과 값이 같아야 한다.(수학적인 의미)
아래와 같은 경우 Pure하다 라고 볼 수 없다. (상태 값에 의존한다 라고 볼 수 있다)
- Case1. 함수가 함수 바깥의 변수를 가져다 사용하는 경우
- Case2. 외부의 값을 변경하려는 경우
*/
//1. 순수 함수
RunSomething runSomething1 = (number) -> {
return number + 10;
};
System.out.println(runSomething1.doIt(1));
//2. 순수 함수가 아닌 경우
int baseNumber1 = 10; //함수 바깥의 변수
RunSomething runSomething2 = new RunSomething() {
int baseNumber2 = 20; //함수 바깥의 변수
@Override
public int doIt(int number) {
baseNumber2++; //Case2. 외부의 값을 변경하려는 경우
return number + baseNumber1; //Case1. 함수 바깥의 값을 가져다 사용하는 경우
}
};
}
}
5. Java가 기본으로 제공하는 함수형 인터페이스
- Java.lang.function 패키지
- 자바에서 미리 정의해둔 자주 사용할만한 함수 인터페이스
- Function<T, R>
- BiFunction<T, U, R>
- Consumer<T>
- Supplier<T>
- Predicate<T>
- UnaryOperator<T>
- BinaryOperator<T>
6. Function<T, R>
- T 타입을 받아서 R 타입을 리턴하는 함수 인터페이스
-> R apply(T t)
- 함수 조합용 메소드
-> andThen
-> compose
7. BiFunction<T, U, R>
- Function<T, R>와 유사하나, 입력값을 2개 받는다.
- 두 개의 값 (T, U)를 받아서 R 타입을 리턴하는 함수
-> R apply(T t, U u)
8. Consumer<T>
- T 타입을 받아서 아무것도 리턴하지 않는 함수 인터페이스
-> void Accept(T t)
- 함수 조합용 메소드
-> andThen
9. Supplier<T>
- T 타입의 값을 제공하는 함수 인터페이스
-> T Get()
10. Predicate<T>
- T 타입의 값을 받아서 boolean을 리턴하는 함수 인터페이스
-> boolean test(T t)
- 함수 조합용 메소드
-> And
-> Or
-> Negate
11. UnaryOperator<T>
- Function<T, R>의 특수한 형태로, 입력값 하나를 받아서 동일한 타입을 리턴하는 함수 인터페이스
12. BinaryOperator<T>
- BiFunction<T, U, R>의 특수한 형태로, 동일한 타입의 입력값 두개를 받아 리턴하는 함수 인터페이스
package me.whiteship.java8to11;
import java.util.function.*;
public class Foo {
public static void main(String[] args) {
//1. 클래스를 만들어서 함수형 프로그래밍
/*
public class Plus10 implements Function<Integer, Integer> {
@Override
public Integer apply(Integer integer) {
return integer + 10;
}
Plus10 plus10 = new Plus10();
System.out.println(plus10.apply(1));
*/
//2. 람다 형식으로 함수형 프로그래밍1 (apply)
/*Function<Integer, Integer> plus10 = (i) -> i+10;
System.out.println(plus10.apply(2));*/
//2. 람다 형식으로 함수형 프로그래밍2 (compose, andThen)
// Compose : 가지고 뒤에 오는 함수 적용하여 계산 후 src의 입력 값으로 사용
Function<Integer, Integer> plus10 = (i) -> i+10;
Function<Integer, Integer> multiply2 = (i) -> i * 2;
//plus10.compose(multiply2); //multiply2 적용 후 plus10 하겠다는 의미
Function<Integer, Integer> multiply2AndPlus10 = plus10.compose(multiply2);
System.out.println(multiply2AndPlus10.apply(2)); // 10 + (2 * 2)
// andThen : 앞의 값 계산 후 뒤의 입력 값으로 사용 (compose와 반대)
Function<Integer, Integer> plus10AndMultiply2 = plus10.andThen(multiply2);
System.out.println(plus10AndMultiply2.apply(2)); // (2 + 10) * 2
Consumer<Integer> printT = (i) -> System.out.println(i);
printT.accept(10); //10 출력
Supplier<Integer> get10 = () -> 15;
System.out.println(get10.get()); //15 출력
Predicate<String> startsWithPark = (s) -> s.startsWith("Park");
//Predicate<Integer> isEven = (i) -> i%2 == 0;
System.out.println(startsWithPark.test("Park"));
// 같은 타입의 인풋, 아웃풋인 경우 UnaryOperator를 써서 좀 더 깔끔하게 처리 가능
//Function<Integer, Integer> plus10 = (i) -> i+10;
UnaryOperator<Integer> unaryPlus10 = (i) -> i+10;
// BiFunction은 3개의 타입이 전부 다를 때 사용하고, BinaryOperator 는 3개의 타입이 전부 같을 때 사용한다.
}
}
14. 람다 표현식
package me.whiteship.java8to11;
import java.util.function.*;
public class Foo {
public static void main(String[] args) {
Foo foo = new Foo();
foo.run();
}
private void run() {
final 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) { //baseNumber 변수는 파라미터로 넘어온 baseNumber를 사용하게 됨 = 섀도잉
System.out.println(baseNumber); //익명 클래스에서 로컬 변수 참조하는 법
}
};
// 람다
IntConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
printInt.accept(30);
/*
3개 클래스 모두 공통적으로 로컬 변수를 참조할 수 있다.
java8 부터 final을 생략할 수 있는 경우가 있는데 해당 변수가 사실상 final인 경우 생략이 가능하다.
사실상 fianl 의미 : 변수 선언 후 값을 변경하는 로직이 없는 경우 (= effective fianl)
람다가 다른 부분은 다른 2개 클래스와 다르게 섀도잉이 불가능하다.
-> 로컬 클래스나 익명 클래스에서 선언한 변수들은 해당 클래스 바깥 혹은 Nested 클래스에서 같은 이름의 변수 사용이 가능하다(덮어써짐 = 섀도잉)
즉 람다는 섀도잉이 안되니 람다의 스콥은 람다를 감싸고 있는 클래스와 동일하다
그렇기 때문에 람다에서 참조 가능한 로컬 변수는 fianl과 effective final 만 사용이 가능하다.(아닐 시 컴파일 에러)
IntConsumer printInt = (baseNumber) -> { //람다는 섀도잉이 안되므로 파라미터 이름을 baseNumber로 넘겨주어도 사용이 불가능하다(컴파일 에러 발생)
System.out.println(baseNumber);
};
*/
}
}
'Java 정리' 카테고리의 다른 글
java 8 정리6 (0) | 2021.05.21 |
---|---|
java 8 정리5 (0) | 2021.05.20 |
java 8 정리4 (0) | 2021.05.19 |
java 8 정리3 (0) | 2021.05.17 |
java 8 정리2 (0) | 2021.05.13 |