일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바
- aws S3
- 카이호스트만
- 자바스크립트
- Git
- db
- 환경 구성
- athena
- 로그 데이터
- s3
- 자바8
- JavaScript
- Study
- aws lambda
- AWS Athena
- Log
- java8
- 환경구성
- #jQuery
- naver smartEditor
- intellij
- 아이비시트
- jQuery
- function
- Java
- 인텔리J
- AWS SQS
- AWS
- AWS Glue
- ibsheet
- Today
- Total
애매한 잡학사전
[자바8] 람다 표현식 - 2장 본문
'카이 호스트만의 코어 자바 8'을 기준으로 정리하였습니다.
6. 자신만의 함수형 인터페이스 구현
- 표준 함수형 인터페이스가 적합하지 않을 경우 직접 구현해야 한다.
/* 함수형 인터페이스 */
@FunctionalInterface
public interface PixelFunction {
Color apply(int x, int y);
}
/* 메서드 구현 */
BufferImage createImage(int width, int height, PixelFunction f) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < hegiht; y++) {
Color color = f.apply(x, y);
image.setRGB(x, y, color.getRGB());
}
}
return image;
}
/* 람다 표현식 */
BufferedImage frenchFlag = createImage(150, 100,
(x, y) -> x < 50 ? color.BLUE : x < 100 ? Color.WHITE : Color.RED);
- 함수형 인터페이스에는 @FunctionalInterface 어노테이션을 붙여야 한다.
- 컴파일러가 어노테이션이 붙은 엔티티를 검사하는데 추상 메서드가 하나만 있는 인터페이스인지 검사한다.
- 자바독 페이지에 해당 인터페이스가 함수형 인터페이스라는 문장을 둔다.
7. 람다 표현식과 변수 유효 범위
7-1. 람다 표현식의 유효 범위
- 람다 표현식의 구현부는 유효 범위가 중첩 블록과 같다.
- 이름 충돌(name conflicts) 규칙과 이름 가리기(shadowing) 규칙이 적용된다.
- 람다 안에 지역 변수와 이름이 같은 파라미터나 지역 변수와 이름이 같은 지역 변수를 선언하는 일은 규칙에 어긋난다.
int first = 0;
Comparator<String> comp = (first, second) -> first.length() - second.length();
- first 를 이미 정의 했기 때문에 람다 내부에 파라미터 first 는 사용할 수 없다.
- 메서드 안에는 이름이 같은 두 지역 변수를 둘 수 없기 때문에 람다 표현식에도 이처럼 이름이 같은 지역 변수를 도입할 수 없다.
public class Application() {
public void doWork() {
Runnable runner = () -> { ...; System.out.println(this.toString()); ... }
}
}
- 람다 표현식 안에 있는 this 키워드는 같은 유효 범위 규칙의 영향을 받는다.
- this 키워드는 람다 자체를 생성하는 메서드의 this 파라미터를 의미한다.
- 표현식 this.toString()은 Runnable 인스턴스가 아니라 Application 객체의 toString 메서드를 호출한다.
- 람다 표현식의 유효 범위는 doWork 메서드 안에 중첩되고, this는 이 메서드 내부 어디서든 의미가 같다.
7-2. 바깥쪽 유효 범위에 속한 변수 접근
public static void repeatMessage(String text, int count) {
Runnable r = () -> {
for (int i = 0; i < count; i++) {
System.out.println(text);
}
};
new Thread(r).start();
}
repeatMessage("Hello", 1000); // 별도의 스레드에서 Hello를 1,000번 출력
- 람다 표현식은 3가지로 구성된다.
1) 코드 블록
2) 파라미터
3) 자유 변수들의 값 ( 자유 변수는 파라미터 변수도 아니고 코드 내부에서 선언한 변수도 아니다 )
- 위 예제에서 람다 표현식은 text와 count라는 자유 변수 두 개를 이용한다.
- 람다 표현식을 표현하는 자료 구조는 반드시 이 변수들의 값을 저장해야 한다.
- 이를 람다 표현식이 이 값들을 캡쳐했다고 말한다.
: 자유 변수 값을 사용하는 코드 블록을 클로저(closure)라고 한다. 자바에서는 람다 표현식이 클로저다.
- 람다 표현식은 자신을 감싸고 있는 유효 범위(enclosing scope)에 속한 변수의 값을 캡쳐할 수 있다.
- 람다 표현식에서는 값이 변하지 않는 변수만 참조할 수 있다.
for (int i = 0; i < n; i++) {
new Thread(() -> System.out.println(i)).start();
}
/* i 변수의 유효 범위가 전체 루프이다. */
- 람다 표현식은 i를 캡처하려고 하지만 i는 값이 변하는 변수이므로 규칙에 어긋난다.
- 람다 표현식을 감싸고 있는 유효 범위에 속한 사실상 최종(effectively final)인 지역 변수에만 접근할 수 있다.
- 사실상 최종 변수는 절대로 수정되지 않기 때문이다.
: 이미 final로 선언했거나 final로 선언이 가능하다.
for (String arg : args) {
new Thread(() -> System.out.println(arg)).start();
}
- 향사된 for 루프의 변수는 유효범위가 단일 반복(single iteration)이므로 사실상 최종이다.
- 위 예에서는 각 반복마다 새로운 변수인 arg가 생성되고, arg 배열에 있는 다음 값을 할당 받는다.
public static void repeatMessage(String text, int count, int threads) {
Runnable r = () -> {
while (count > 0) {
count--; // 오류 - 캡처한 변수를 변경할 수 없다.
System.out.println(text);
}
};
for ( int i = 0; i < threads; i++) {
new Thread(r).start();
}
}
- 사실상 최종(effectively final) 규칙 때문에 람다 표현식은 캡처한 변수를 어느 것도 변경할 수 없다.
: 컴파일러가 모든 병행 접근 오류를 잡아내리라고 확신하면 안된다.
: 캡처한 변수를 변경할 수 없는 것은 지역 변수에만 해당한다.
: count가 외부 클래스의 인스턴스 변수나 정적 변수라면, 결과가 정의되지 않더라도 아무 오류도 보고하지 않는다.
8. 고차함수
- 메서드에 숫자를 전달하고, 숫자를 생성하는 메서드를 만들 수 있는 것처럼 함수를 인자와 반환 값으로 사용할 수 있다.
- 함수를 처리하거나 반환하는 함수를 고차 함수라고 한다.
8-1. 함수를 반환하는 메서드
public static Comparator<String> compareInDirection(int direction) {
return (x, y) -> direction * x.compareTo(y);
}
- compareInDirection(1) 호출은 오름차순 비교자를 compareInDirection(-1) 호출은 내림차순 비교자를 반환한다.
Arrays.sort(friends, compareInDirection(-1));
- 위의 예제처럼 비교자 인터페이스를 인자로 받는 메서드에 전달할 수 있다.
8-2. 함수를 수정하는 메서드
public static Comparator<String> reverse(Comparator<String> comp) {
return (x, y) -> comp.compare(x, y);
}
- 위 예제는 함수를 인자로 받아서 수정된 함수를 반환 한다.
reverse(String::compareToIgnoreCase);
- 대소문자를 구별하지 않는 내림차순 비교자를 얻으려면 위 예제처럼 호출해야 한다.
9. 지역클래스
- 메서드 안에 정의한 클래스를 지역 클래스(로컬 클래스)라고 한다.
- 보통은 그저 전술적으로 사용하는 클래스를 지역 클래스로 정의한다.
private static Random generator = new Random();
public static IntSequence randomInts(int low, int high) {
class RandomSequence implements IntSequence {
public int next() {
return low + generator.nextInt(high - low + 1);
}
public boolean hasNext() {
return true;
}
}
return new RandomSequence();
}
- 지역 클래스는 메서드 바깥에서 접근할 수 없으므로 public 이나 private으로 선언할 수 없다.
- 클래스를 지역 클래스로 만들면 두 가지 이점이 있다.
- 첫 번째 : 클래스 이름이 메서드의 유효 범위 안으로 숨는다.
- 두 번째 : 람다 표현식의 변수와 마찬가지로 지역 클래스의 메서드에서 지역 클래스를 감싸고 있는 유효 범위에 속한 변수에 접근할 수 있다.
10. 익명클래스
- 딱 한번만 사용할 목적으로 생성할 클래스는 익명 클래스로 만들 수 있다.
private static Random generator = new Random();
public static IntSequence randomIntsIint low, int high) {
return new IntSequence() {
public int next() {
return low + generator.nextInt(high - low + 1);
}
public boolean hasNext() {
return true;
}
}
}
- 자바에 람다 표현식이 생기기 전에는 익명 내부 클래스가 러너블(runnable), 비교자(comparator), 함수 객체(functional object)를 제공하는 가장 간결한 문법이었다.
'DEV > JAVA' 카테고리의 다른 글
java에서 Database 접속 구현하기 with 인터페이스 (2) | 2022.09.13 |
---|---|
[자바8] 람다 표현식 - 1장 (0) | 2022.02.13 |
[자바8] 인터페이스 java 8 interface - 2장 (0) | 2022.01.27 |
[자바8] 인터페이스 java 8 interface - 1장 (0) | 2022.01.24 |
AWS Personalize putUsers 처리 Importing users incrementally (0) | 2021.10.26 |