검색결과 리스트
Java 정리에 해당되는 글 12건
- 2021.05.17 java 8 정리3
- 2021.05.13 java 8 정리2
- 2021.05.12 java 8 정리1
글
java 8 정리3
인프런 강의 6일차.
- 더 자바, Java 8 (백기선 강사님)
1. 인터페이스 기본 메소드와 스태틱 메소드 2
- Iterable의 기본 메소드
-> forEach()
-> spliterator()
- Collection의 기본 메소드(Collection은 Iterable의 상속받는 클래스이니 사실상 Collection = Iterable과 같다)
-> stream() / parallelStream()
-> removeIf(Predicate)
-> spliterator()
- Comparator의 기본 메소드
-> reversed()
-> thenComparing()
-> static reverseOrder() / naturalOrder()
-> static nullsFirst() / nullsLast()
-> static comparing()
package me.whiteship.java8to11;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
/*GreetingInterface greetingInterface = new DefaultGreeting("park");
greetingInterface.printName(); //park 출력
greetingInterface.printNameUpperCase(); //PARK 출력
GreetingInterface.printAnything(); //Interface의 static 메소드 바로 사용*/
List<String> name = new ArrayList<>();
name.add("park");
name.add("dong");
name.add("hyeon");
name.add("kim");
//case1. foreach lambda
name.forEach(s -> {
//System.out.println(s);
});
//case2. foreach method reference
//name.forEach(System.out::println);
Spliterator<String> spliterator = name.spliterator();
//case3. spliterator 사용(name 배열을 순회하면서 출력한다)
//tryAdvance = hasNext와 같은 역할
//while(spliterator.tryAdvance(System.out::println));
/* spliterator.trySplit()를 사용한 split 시 배열에 대해 trySplit을 진행한 뒤 tryAdvance를 수행해야 한다.
즉 아래 코드는 오류가 발생한다.
Spliterator<String> spliterator = name.spliterator();
while(spliterator.tryAdvance(System.out::println));
Spliterator<String> spliterator1 = spliterator.trySplit(); //name 배열에 대해 split 진행
while(spliterator1.tryAdvance(System.out::println));
--------------------------------------------------------------
//정상 동작 쿼리
Spliterator<String> spliterator = name.spliterator();
Spliterator<String> spliterator1 = spliterator.trySplit(); //name 배열에 대해 split 진행
while(spliterator.tryAdvance(System.out::println));
while(spliterator1.tryAdvance(System.out::println));
*/
//name 배열에 대해 UppserCase실행 후 filer를 걸어서 P로 시작하는 값만 읽어서 count
name.stream().map(String::toUpperCase)
.filter(s -> s.startsWith("P"))
.count();
//k로 시작하는 문자열 제거
name.removeIf(s -> s.startsWith("k"));
name.forEach(System.out::println);
/*park
dong
hyeon*/
//case1. 문자열 역순으로 소팅
name.sort(String::compareToIgnoreCase);
name.forEach(System.out::println);
/*dong
hyeon
park
*/
//case2. 문자열 역순으로 소팅(Comparator 사용)
Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
name.sort(compareToIgnoreCase.reversed());
name.forEach(System.out::println);
/*park
hyeon
dong
*/
//case3. 문자열 역순으로 소팅 reversd() 사용 후 thenComparing으로 추가 비교
//name.sort(compareToIgnoreCase.reversed().thenComparing());
name.forEach(System.out::println);
}
}
2. 스트림 API
- Stream : sequence of elements supporting sequential and parallel aggregate operations
-> 데이터를 담고 있는 저장소(컬렉션)이다.
-> Function in nature, 스트림이 처리하는 데이터 소스를 변경하지 않는다.
-> 스트림으로 처리하는 데이터는 오직 한번만 처리한다.
-> 무제한일 수도 있다. (Sort Circuit 메소드를 사용해서 제한할 수 있다.)
-> 중개 오퍼레이션은 근본적으로 lazy하다.
-> 손쉽게 병렬처리할 수 있다.
- 스트림 파이프라인
-> 0 또는 다수의 중개 오퍼레이션(intermediate operation)과 한개의 종료 오퍼레이션(terminal operation)으로 구성.
-> 스트림의 데이터 소스는 오직 터미널 오퍼레이션을 실행할 때에만 처리한다.
- 중개 오퍼레이션
-> Stream을 리턴한다.
-> Stateless / Statefull 오퍼레이션으로 더 상세하게 구분할 수도 있다. (대부분은 Stateless지만 distinct나 sorted처럼 이전 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Statefull 오퍼레이션이다)
-> filter, map, limit, skip, sorted, ...
- 종료 오퍼레이션
-> Stream을 리턴하지 않는다.
-> collect, allMatch, count, forEach, min, max, ...
List<String> name = new ArrayList<>();
name.add("park");
name.add("dong");
name.add("hyeon");
name.add("kim");
//Stream<String> stringStream
name.stream().map((s) -> {
System.out.println(s); //map은 중개 오퍼레이션이기 때문에 터미널 오퍼레이션이 오기전까지 실행되지 않는다. 즉 해당 sout은 출력되지 않는다!
return s.toUpperCase();
}); //해당 상태는 종료 오퍼레이션 없이 끝난 스트림이다.(선언만 된 상태이다)
List<String> collect = name.stream().map((s) -> {
System.out.println(s); //map은 중개 오퍼레이션이기 때문에 터미널 오퍼레이션이 오기전까지 실행되지 않는다. 즉 해당 sout은 출력되지 않는다!
return s.toUpperCase();
}).collect(Collectors.toList());//종료 오퍼레이션인 collect 추가했기 때문에 처리 진행
//return이 upperCase이므로 sout 당시는 소문자가 출력되고, return값인 collect 변수 출력 시 대문자가 출력된다.
name.forEach(System.out::println); //대문자 출력
//병렬 스트림
List<String> collect1 = name.parallelStream().map((s)->{
System.out.println(s + " " + Thread.currentThread().getName()); //ForkJoinPool을 통해 다른 thread에서 실행됨을 확인 가능
return s.toUpperCase();
}).collect(Collectors.toList());
collect1.forEach(System.out::println);
package me.whiteship.java8to11;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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));
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("spring 으로 시작하는 수업");
springClasses.stream()
.filter(oc -> oc.getTitle().startsWith("spring"))
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("close 되지 않은 수업");
springClasses.stream()
.filter(Predicate.not(OnlineClass::isClosed))// filter(oc -> !oc.isClosed())와 같은 의미이다.
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("수업 이름만 모아서 스트림 만들기");
//map이라는 중개 오퍼레이션을 사용해야함
springClasses.stream()
.map(oc -> oc.getTitle()) //input은 onlineClass로 들어오나 output은 다른 type으로 변경가능
//.forEach(s -> System.out.println(s)); //getTitle이 String이기 때문에 들어온 String 출력
.forEach(System.out::println); //method preference
System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
//keesunEvents는 리스트 2개를 모아놓은 리스트이고, 이 2개를 합쳐서 1개의 스트림으로 만들어야한다. -> Flat 시킨다
keesunEvents.stream()
.flatMap(Collection::stream) //리스트를 flat 시켜 1개의 스트림으로 만들음
.forEach(oc -> System.out.println(oc.getId())); //input이 collection stream으로 들어감
System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");
//Stream의 iterator를 가지고 구현 가능
Stream.iterate(10, i -> i + 1) //10부터 1씩 증가하는 무제한 스트림 (중개 오퍼레이션이기 때문에 iterate만 있어도 별다른 실행이 되지 않는다.
.skip(10)
.limit(10)
.forEach(System.out::println);
System.out.println("자바 수업 중에 Test가 들어있는 수업이 있는지 확인");
//Stream의 Match로 구현 가능
boolean test = javaClasses.stream().anyMatch(oc -> oc.getTitle().contains("Test"));
System.out.println(test);
System.out.println("스프링 수업 중에 제목에 spring이 들어간 제목만 모아서 List로 만들기");
//case1. filter 먼저 수행 (filter에서 onlineClass로 비교 수행)
List<String> spring = springClasses.stream()
.filter(oc -> oc.getTitle().contains("spring")) //filter을 먼저 해도 되고, map을 먼저 해도 됨. 다만 어떤 것을 먼저 하느냐에 따라 지나가는 타입이 달라지므로 구현에 주의!
.map(OnlineClass::getTitle)
.collect(Collectors.toList());
//case2. map 먼저 수행 (filter에서 String 으로 비교 수행)
List<String> spring1 = springClasses.stream()
.map(OnlineClass::getTitle)
.filter(t -> t.contains("spring"))
.collect(Collectors.toList());
}
}
'Java 정리' 카테고리의 다른 글
java 8 정리6 (0) | 2021.05.21 |
---|---|
java 8 정리5 (0) | 2021.05.20 |
java 8 정리4 (0) | 2021.05.19 |
java 8 정리2 (0) | 2021.05.13 |
java 8 정리1 (0) | 2021.05.12 |
설정
트랙백
댓글
글
java 8 정리2
인프런 강의 5일차.
- 더 자바, Java 8 (백기선 강사님)
1. 메소드 레퍼런스
- 람다가 하는 일이 기존 메소드 또는 생성자를 호출하는 거라면, 메소드 레퍼런스를 사용해서 매우 간결하게 표현가능
- 메소드 레퍼런스 참조 방법
스태틱 메소드 참조 | 타입::스태틱 메소드 |
특정 객체의 인스턴스 메소드 참조 | 객체 레퍼런스::인스턴스 메소드 |
임의 객체의 인스턴스 메소드 참조 | 타입::인스턴스 메소드 |
생성자 참조 | 타입::new |
-> 메소드 또는 생성자의 매개변수로 람다의 입력값을 받는다
-> 리턴값 또는 생성한 객체는 람다의 리턴값이다.
package me.whiteship.java8to11;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
public class App {
public static void main(String[] args) {
UnaryOperator<String> hi = (s) -> "hi " + s;
UnaryOperator<String> staticMethodHi = Greeting::hi; //hi와 staticMethodHi는 동일한 결과를 같는다.
Supplier<Greeting> newGreeting = Greeting::new; //Supplier는 선언하더라도 아무런 일이 발생하지 않는다.
//newGreeting.get() 해야 뭐라도 만들어짐.
Greeting greeting = new Greeting();
UnaryOperator<String> hello = greeting::hello;
System.out.println(hello.apply("Park"));
Function<String, Greeting> parkGreeting = Greeting::new; //Supplier와 똑같은 Greeting::new 이지만 다른 생성자를 참조한다.
Greeting park = parkGreeting.apply("park");
System.out.println(park.getName());
String[] names = {"park", "whiteship", "toby"};
//case1. names를 정렬시킬 때 특정한 조건(compare에 구현된 조건)으로 정렬
/*Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return 0;
}
}*/
//case2. new Comparator를 람다로 치환
Arrays.sort(names, (o1, o2) -> 0);
//case3. case2를 메소드 레퍼런스로 가져다 사용
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names));
}
}
2. 인터페이스 기본 메소드와 스태틱 메소드
- 기본 메소드(Default Method)
-> 메소드 또는 생성자의 매개변수로 람다의 입력값을 받는다
-> 해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.
* 컴파일 에러는 아니지만 구현체에 따라 런타임 에러가 발생할 수 있다.
* 반드시 문서화 할 것. (@ImplSpec 자바독 태그 사용)
-> Object class가 제공하는 기능 (equals, hasCode)는 기본 메소드로 제공할 수 없다.
* 구현체가 재정의해야 한다.
-> 본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.
-> 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
-> 인터페이스 구현체가 재정의 할 수도 있다.
- 스태틱 메소드
-> 해당 타입 관련 헬터 또는 유틸리티 메소드를 제공할 때 인터페이스에 스태틱 메소드를 제공할 수 있다.
package me.whiteship.java8to11;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
public class App {
public static void main(String[] args) {
GreetingInterface greetingInterface = new DefaultGreeting("park");
greetingInterface.printName(); //park 출력
greetingInterface.printNameUpperCase(); //PARK 출력
GreetingInterface.printAnything(); //Interface의 static 메소드 바로 사용
}
}
package me.whiteship.java8to11;
public interface GreetingInterface {
void printName();
//case1. 만약 printName()만 존재하는 인터페이스에 새로 추상 메소드를 추가할 경우 해당 인터페이스를 구현한 모든 클래스는 컴파일 에러가 발생한다(추가된 메소드를 구현하지 않았기 때문!)
//void printNameUpperCase();
//case2. 헌데 모든 클래스에 새로 추가되는 추상 메소드를 구현하지 않아도 컴파일 에러를 발생시키지 않는 방법은 Default 메소드로 선언하는 것이다.
/*default void printNameUpperCase(){
System.out.println("default Greeting");
}*/
/**
* @implSpec
* 이 구현체는 getName()으로 가져온 문자열을 대문자로 바꿔 출력한다.
* 기본 메소드는 구현체가 모르게 추가된 기능이기 때문에 null로 온 경우 런타임 익셉션이 발생할 수 있다.(문서화 필수!)
*/
//case3. 아래와 같은 형태로도 사용이 가능하다.
default void printNameUpperCase(){
System.out.println(getName().toUpperCase());
}
//static 메소드
static void printAnything(){
System.out.println("GreetingInterface");
}
String getName();
}
package me.whiteship.java8to11;
public interface Bar {
//GreetingInterface가 제공하는 기본 구현체를 제공하고 싶지 않을 경우 Bar에서 해당 구현체를 다시 추상 메소드로 구현하면 된다 extends GreetingInterface
//void printNameUpperCase() ;
//아무것도 선언하지 않을 시 기본 오버라이딩
//만약 Bar와 GreetingInterface에 같은 이름의 기본 구현체가 있고, 이를 implements하는 경우 에러가 발생한다.
default void printNameUpperCase(){
System.out.println("BAR");
}
}
package me.whiteship.java8to11;
public class DefaultGreeting implements GreetingInterface, Bar{
//Bar를 추가한 경우 두 인터페이스에 같이 구현된 기본 구현체인 printNameUpperCase 충돌이 발생한다.
//충돌이 발생하는 경우 DefaultGreeting에서 직접 기본 구현체를 오버라이딩해서 구현해야 한다.
String name;
public DefaultGreeting(String name) {
this.name = name;
}
@Override
public void printName() {
System.out.println(this.name);
}
//GreetingInterface, Bar에 printNameUpperCase라는 기본 구현체가 있으므로 오버라이딩 해야 컴파일 오류가 발생하지 않음
@Override
public void printNameUpperCase() {
}
@Override
public String getName() {
return this.name;
}
}
'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 정리1 (0) | 2021.05.12 |
설정
트랙백
댓글
글
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 |