소개
이 학습 가이드는 다른 기능적 인터페이스에서 존재하는 Java8 뿐만 아니라,자신의 일반적인 사용 경우,그리고 사용법에서 표준 JDK 라이브러리입니다.
추가 읽기:
반복 가능한 스트림에서 Java
는 방법을 사용하면 다른 논리에 Java8Streams
람다 Java8
Java8 가져 새롭고 강력한 구문론적인 개선의 형태로 람다 식입니다. 람다는 우리가 일류 언어 시민으로 처리 할 수있는 익명 함수입니다. 예를 들어,우리는 그것을 전달하거나 메소드에서 반환 할 수 있습니다.
Java8 이전에는 일반적으로 단일 기능 조각을 캡슐화해야하는 모든 경우에 대한 클래스를 만듭니다. 이것은 원시 함수 표현의 역할을하는 것을 정의하기 위해 많은 불필요한 상용구 코드를 암시했습니다.
“Lambda Expressions and Functional Interfaces:Tips and Best Practices”기사에서는 lambdas 작업의 기능적 인터페이스와 모범 사례를보다 자세히 설명합니다. 이 가이드는 java 에 존재하는 특정 기능 인터페이스에 중점을 둡니다.우틸.기능 패키지.
기능 인터페이스
모든 기능 인터페이스에 유익한@FunctionalInterface 주석이있는 것이 좋습니다. 이 명확하게 전달하는 목적의 인터페이스,또한 컴파일러를 오류를 생성하는 경우 이 주석을 추가 인터페이스를 만족하지 않는 조건이 있습니다.
SAM(단일 추상 메소드)이있는 모든 인터페이스는 기능적 인터페이스이며,그 구현은 람다 표현식으로 취급 될 수 있습니다.
주는 Java8 의 기본 방법은 추상적이고 포함되지 않습;기능적 인터페이스는 여전히있을 수 있습니다 여러 기본 방법이 있습니다. 우리는 함수의 문서를 보면서 이것을 관찰 할 수 있습니다.
기능
가장 간단하고의 일반적인 경우 람다는 기능과 인터페이스 메서드를 받는 하나의 값을 반환합니다. 이 함수의 인수 표시 기능에 의해 인터페이스를 매개 변수가 있의 유형에 의하여 인수 및 반환 값:
public interface Function<T, R> { … }
중 하나의 사용법의 기능에 입력 표준 라이브러리입니다.computeIfAbsent 방법. 이 메서드는 키로 맵에서 값을 반환하지만 키가 맵에 아직없는 경우 값을 계산합니다. 계산 값을 사용하여 전달 기능을 구현:
Map<String, Integer> nameMap = new HashMap<>();Integer value = nameMap.computeIfAbsent("John", s -> s.length());
이 경우에,우리는 것이 값을 계산하여 적용하는 기능 키를 안에 넣어 지도하고,또한 메서드에서 반환된다. 람다를 전달 및 반환 된 값 유형과 일치하는 메소드 참조로 대체 할 수 있습니다.
우리가 메소드를 호출하는 객체는 사실 메소드의 암시 적 첫 번째 인수라는 것을 기억하십시오. 이를 통해 인스턴스 메소드 길이 참조를 함수 인터페이스에 캐스팅 할 수 있습니다:
Integer value = nameMap.computeIfAbsent("John", String::length);
기능을 인터페이스도는 기본적으로 작성할 수 있는 방법을 결합 여러 가지 기능을 하나로 그들을 실행 순차적으로.
Function<Integer, String> intToString = Object::toString;Function<String, String> quote = s -> "'" + s + "'";Function<Integer, String> quoteIntToString = quote.compose(intToString);assertEquals("'5'", quoteIntToString.apply(5));
quoteIntToString 기능은 조합의 견적 기능에 적용하는 결과의 intToString 기능입니다.
기본 기능을 전문화
이후에는 기본 형식할 수 없는 일반적인 유형을 인수,버전이 있습니다 함수의 인터페이스를 위해 가장 많이 사용되는 기본 형식 더블,int,long,그리고 그들의 조합에서의 인수 및 반환 유형:
- IntFunction, LongFunction, DoubleFunction: arguments are of specified type, return type is parameterized
- ToIntFunction, ToLongFunction, ToDoubleFunction: return type is of specified type, arguments are parameterized
- DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction, LongToIntFunction, LongToDoubleFunction: 데이 모두 인수 및 반환 정의된 형식으로 기본 형식으로에 의해 지정된 자신의 이름
을 예로 들어가 없다,out-of-the-box 기능적 인터페이스 기능이 걸리는 짧은 반환하는 바이트를,하지만 아무것도 우리를 중지에서 쓰는 우리 자신의:
@FunctionalInterfacepublic interface ShortToByteFunction { byte applyAsByte(short s);}
이제 우리는 쓸 수 있는 방법을 변형시키는 배열의 짧은 바이트의 배열을 사용하여 원칙에 의해 정의된 ShortToByteFunction:
public byte transformArray(short array, ShortToByteFunction function) { byte transformedArray = new byte; for (int i = 0; i < array.length; i++) { transformedArray = function.applyAsByte(array); } return transformedArray;}
여기에 어떻게 우리가 그것을 사용할 수 있을 변환하는 배열의 반바지를 바이트의 배열을 곱 2:
short array = {(short) 1, (short) 2, (short) 3};byte transformedArray = transformArray(array, s -> (byte) (s * 2));byte expectedArray = {(byte) 2, (byte) 4, (byte) 6};assertArrayEquals(expectedArray, transformedArray);
두-인자 수능 특성화
정의하는 람다 식으로 두 개의 인수,우리가 사용하여 추가적인 인터페이스를 포함하는”비”키워드를 이름:BiFunction,ToDoubleBiFunction,ToIntBiFunction 및 ToLongBiFunction.
BiFunction 에는 인수와 반환 유형이 모두 일반화되어 있지만 ToDoubleBiFunction 및 기타에서는 원시 값을 반환 할 수 있습니다.
표준 API 에서이 인터페이스를 사용하는 일반적인 예 중 하나는 맵에 있습니다.맵의 모든 값을 일부 계산 된 값으로 대체 할 수있는 replaceAll 메서드입니다.
키와 이전 값을 수신하는 BiFunction 구현을 사용하여 급여에 대한 새 값을 계산하고 반환 해 보겠습니다.
Map<String, Integer> salaries = new HashMap<>();salaries.put("John", 40000);salaries.put("Freddy", 30000);salaries.put("Samuel", 50000);salaries.replaceAll((name, oldValue) -> name.equals("Freddy") ? oldValue : oldValue + 10000);
공급자
공급자 기능적 인터페이스는 또 다른 기능을 전문화하지 않는 모든 인수입니다. 우리는 일반적으로 게으른 가치 생성을 위해 그것을 사용합니다. 예를 들어 이중 값을 제곱하는 함수를 정의해 보겠습니다. 그것은 가치 자체를받지 않을 것이지만,이 가치의 공급 업체:
public double squareLazy(Supplier<Double> lazyValue) { return Math.pow(lazyValue.get(), 2);}
이것은 우리 지연을 생성하는 인수에 대한 호출이 기능을 사용하여 공급자를 구현합니다. 인수의 생성에 상당한 시간이 걸리는 경우 유용할 수 있습니다. 우리는 시뮬레이션을 사용하는 것 구아바의 sleepUninterruptibly 방법:
Supplier<Double> lazyValue = () -> { Uninterruptibles.sleepUninterruptibly(1000, TimeUnit.MILLISECONDS); return 9d;};Double valueSquared = squareLazy(lazyValue);
다른 사용 사례에 대한 공급자가 정의 논리를 위한 순서 세대입니다. 그것을 보여주기 위해 정적 스트림을 사용합시다.생성 방법 스트림을 만드는 피보나치 숫자:
int fibs = {0, 1};Stream<Integer> fibonacci = Stream.generate(() -> { int result = fibs; int fib3 = fibs + fibs; fibs = fibs; fibs = fib3; return result;});
기능을 우리는 전달합니다.생성 방법은 공급 업체 기능 인터페이스를 구현합니다. 발전기로 유용하기 위해서는 공급 업체가 일반적으로 일종의 외부 상태를 필요로한다는 점에 유의하십시오. 이 경우,그 상태는 마지막 두 피보나치 시퀀스 번호로 구성됩니다.
를 구현하는 이 국가,우리가 사용하는 대신 부부의 변수하기 때문에 모든 외부 변수 내에서 사용하는 람다를 효과적으로 최종입니다.
기타 전문의 공급자 기능적 인터페이스를 포함 BooleanSupplier,DoubleSupplier,LongSupplier 및 IntSupplier,그 반환을 유형은 해당하는 기본 형식.
소비자
반대로 공급 업체,소비자 받 generified 인수하고 아무 것도 반환하지 않습니다. 부작용을 나타내는 기능입니다.
예를 들어,콘솔에 인사말을 인쇄하여 이름 목록에있는 모든 사람들에게 인사합시다. 람다가 목록에 전달되었습니다.forEach 방법을 구현한 소비자 기능적 인터페이스:
List<String> names = Arrays.asList("John", "Freddy", "Samuel");names.forEach(name -> System.out.println("Hello, " + name));
도 있 전문화된 버전의 소비자 DoubleConsumer,IntConsumer 및 LongConsumer—을 받는 기본값으로 인수입니다. 더 흥미로운 것은 BiConsumer 인터페이스입니다. 하나 그것의 사용의 경우는 반복하는 항목의 지도:
Map<String, Integer> ages = new HashMap<>();ages.put("John", 25);ages.put("Freddy", 24);ages.put("Samuel", 30);ages.forEach((name, age) -> System.out.println(name + " is " + age + " years old"));
또 다른 설정의 전문 BiConsumer 버전으로 구성 ObjDoubleConsumer,ObjIntConsumer 및 ObjLongConsumer 는 받는 두 개의 인수;인수 중 하나입니다 generified,및 다른 사람은 기본 유형입니다.
술어
수학 논리에서 술어는 값을 받고 부울 값을 반환하는 함수입니다.
술어 기능 인터페이스는 일반화 된 값을 수신하고 부울을 반환하는 함수의 전문화입니다. 일반적으로 사용하는 경우의 조건자는 람다를 필터 컬렉션의 값:
List<String> names = Arrays.asList("Angela", "Aaron", "Bob", "Claire", "David");List<String> namesWithA = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList());
위 코드에서는,우리는 필터 목록을 사용하여 스트림 API 및 유지는 이름만으로 시작하는 문자””. 술어 구현은 필터링 논리를 캡슐화합니다.
앞의 모든 예제와 마찬가지로 원시 값을 수신하는이 함수의 IntPredicate,DoublePredicate 및 LongPredicate 버전이 있습니다.
연산자
연산자 인터페이스는 동일한 값 유형을 수신하고 반환하는 함수의 특수한 경우입니다. UnaryOperator 인터페이스는 단일 인수를 수신합니다. 하나 그것의 사용의 경우에 컬렉션의 API 를 대체하는 모든 값 목록에서 일부로 계산된 동일한 유형의 값:
List<String> names = Arrays.asList("bob", "josh", "megan");names.replaceAll(name -> name.toUpperCase());
니다.이 자리에 값을 대체로 replaceall 함수는 void 를 반환합니다. 목적에 맞게 목록의 값을 변환하는 데 사용되는 람다는 수신하는 것과 동일한 결과 유형을 반환해야합니다. 이것이 UnaryOperator 가 여기서 유용한 이유입니다.물론 이름 대신
->이름입니다.toUpperCase(),우리는 단순히 메소드 참조를 사용할 수 있습니다:
names.replaceAll(String::toUpperCase);
BinaryOperator 의 가장 흥미로운 사용 사례 중 하나는 축소 작업입니다. 모든 값의 합계에 정수 모음을 집계하고 싶다고 가정 해보십시오. 와 스트림 API,우리가 할 수 있을 사용하여 이를 수집하지만,일반적인 방법 그것을 할 사용하는 것입니다 줄이기 위한 방법:
List<Integer> values = Arrays.asList(3, 5, 8, 9, 12);int sum = values.stream() .reduce(0, (i1, i2) -> i1 + i2);
줄이기 위한 방법 받는 초기 어큐뮬레이터 값과 BinaryOperator 기능입니다. 이 함수의 인수는 동일한 유형의 값 쌍입니다; 함수 자체에는 동일한 유형의 단일 값으로 결합하기위한 논리도 포함되어 있습니다. 전달된 기능이 있어야합니다 연관된 것을 의미하는 순서의 값을 집계하지 않는 문제,즉 다음과 같은 조건을 보유해야
op.apply(a, op.apply(b, c)) == op.apply(op.apply(a, b), c)
포럼에 대한 액세스를 제공의 BinaryOperator 운전자 기능을 할 수 있는 우리들을 쉽게 병렬화하 감소 프로세스.
의 과정,또한 전문 분야의 UnaryOperator 및 BinaryOperator 과 함께 사용할 수 있는 원시적인 가치,즉 DoubleUnaryOperator,IntUnaryOperator,LongUnaryOperator,DoubleBinaryOperator,IntBinaryOperator 및 LongBinaryOperator.
레거시 기능 인터페이스
모든 기능 인터페이스가 Java8 에 등장한 것은 아닙니다. 이전 버전의 Java 의 많은 인터페이스는 FunctionalInterface 의 제약 조건을 준수하며 lambdas 로 사용할 수 있습니다. 눈에 띄는 예로는 동시성 Api 에 사용되는 실행 가능 및 호출 가능 인터페이스가 있습니다. Java8 에서 이러한 인터페이스는@FunctionalInterface 주석으로도 표시됩니다. 이를 통해 동시성 코드를 크게 단순화 할 수 있습니다.
Thread thread = new Thread(() -> System.out.println("Hello From Another Thread"));thread.start();