@FunctionalInterface
API
로 제공 java.util.function
::
쓰면 자료형만 기재 가능✨ (암기)
- 파라미터 있고 리턴 없음
Consumer<T>
: 인자만 있고 반환값이 없는 인터페이스
Consumer
// java.util.function.Consumer 인터페이스
/*
* Consumer<T> : void accept(T t) : T를 파라미터로 받아서 소비
* BiConsumer<T,U>: void accept(T t, U u), T와 U를 파라미터로 받아서 소비
* DoubleConsumer: void accept(double d) : double를 파라미터로 받아서 소비
* IntConsumer: void accept(int d) : int를 파라미터로 받아서 소비
* LongConsumer: void accept(long d) : long를 파라미터로 받아서 소비
* ObjDoubleConsumer<T> : void accept(T t, double) : T와 double를 파라미터로 받아서 소비
* ObjIntConsumer<T> : void accept(T t, int) : T와 int를 파라미터로 받아서 소비
* ObjLongConsumer<T> : void accept(T t, long) : T와 long를 파라미터로 받아서 소비
*/
//1. Consumer
//익명 클래스
Consumer<String> c = new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println("Consumer:" + t);
}
};
c.accept("hello");
//lambda 표현식
Consumer<String> c2 = t -> {
System.out.println("Consumer_lambda:" + t);
};
c2.accept("hello2");
BiConsumer<T,U>: void accept(T t, U u)
두 개의 인자
를 받아서 소비하는 accept 메서드를 제공합니다. 해당 예제에서는 익명 클래스와 lambda 표현식을 사용하여 BiConsumer 객체를 생성하고 accept 메서드를 호출하는 방법을 보여줍니다. //2. BiConsumer
BiConsumer<String, String> b = new BiConsumer<String, String>() {
@Override
public void accept(String t, String u) {
System.out.println("BiConsumer:"+ t +":" + u);
}
};
BiConsumer<String, String> b2 =
(t, u) ->System.out.println("BiConsumer_lambda:"+ t +":" + u);
b2.accept("hello", "world");
ObjIntConsumer<T> : void accept(T t, int)
//3. ObjIntConsumer
ObjIntConsumer<String> x = (s, i)->System.out.println(s+"\t"+i);
x.accept("hello", 10);
- 파라미터 있고 리턴 존재
// java.util.function.Function 인터페이스 : 파라미터 있고 리턴 존재
/*
Function<T, R> : R apply(T t) , T를 R로 매핑
BiFunction<T,U, R> : R apply(T t, U u), T와 U를 R로 매핑
DoubleFunction<R> : R apply(double) , double를 R로 매핑
IntFunction<R> : R apply(int) , int를 R로 매핑
LongFunction<R> : R apply(long) , long를 R로 매핑
LongToDoubleFunction : double applyAsDouble(long value),long를 double로 매핑
IntToDoubleFunction : double applyAsDouble(int value), int를 double로 매핑
...
*/
Function, BiFunction, IntToDoubleFunction 등은 각각 파라미터로 받는 데이터 타입에 따라 R로 매핑해주는 인터페이스입니다.
이를 활용하면 일반적인 객체 지향 프로그래밍의 메소드처럼, 파라미터를 받아 결과값을 리턴하는 함수를 만들 수 있습니다.
이 코드에서는 각각의 함수형 인터페이스에 대해 익명클래스와 람다 표현식으로 구현하는 방법을 보여줍니다. 이를 통해 코드를 간결하게 만들 수 있고, 가독성을 높일 수 있습니다.
Function 인터페이스를 활용한 예시로는, 문자열의 길이를 구하는 코드
//1. Function<T, R>
// 익명클래스
Function<String, Integer> f = new Function<String, Integer>() {
@Override
public Integer apply(String t) {
return t.length();
}
};
System.out.println(f.apply("hello"));
// lambda 표현식
Function<String, Integer> f2 = t -> t.length();
System.out.println(f2.apply("helloworld"));
// BiFunction
BiFunction<String, String, Integer> x = new BiFunction<String, String, Integer>() {
@Override
public Integer apply(String t, String u) {
return (t+u).length();
}
};
BiFunction<String, String, Integer> x2 = (t, u)-> (t+u).length();
System.out.println(x2.apply("hello", "world"));
// IntToDoubleFunction
IntToDoubleFunction y = new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
return value + 0.0;
}
};
IntToDoubleFunction y2 = v -> v+ 0.0;
System.out.println(y2.applyAsDouble(100));
- 파라미터 있고 리턴 존재
// java.util.function.Operator 인터페이스 : 파라미터 있고 리턴 존재
// Operator와 Function과는 상속관계
/*
BinaryOperator<T> : BiFunction<T,T,T> 동일,
T apply(T, T)
DoubleBinaryOperator: double applyAsDouble(double left, double right)
IntBinaryOperator: int applyAsInt(int left, int right)
LongBinaryOperator: long applyAsLong(long left, long right)
UnaryOperator<T> : Function<T,T> 동일
T apply(T)
DoubleUnaryOperator: double applyAsDouble(double left, double right
IntUnaryOperator: int applyAsInt(int left)
LongUnaryOperator: long applyAsLong(long left)
*/
// 익명 클래스
BinaryOperator<Integer> x = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t, Integer u) {
return t+u;
}
};
// lambda 표현식
BinaryOperator<Integer> x2 = (t, u) -> t+u;
System.out.println(x2.apply(10, 20));
IntBinaryOperator y = (n, n2)-> n+n2;
System.out.println(y.applyAsInt(10, 20));
UnaryOperator<T>
: Function<T,T>
동일// UnaryOperator<T>
UnaryOperator<Integer> k = new UnaryOperator<Integer>() {
@Override
public Integer apply(Integer t) {
return t + 100;
}
};
UnaryOperator<Integer> k2 = t -> t+100;
System.out.println(k2.apply(100));
UnaryOperator를 사용하여 인자로 정수를 받아들이고 100을 더해 같은 타입으로 반환하는 k와 k2 변수가 선언됩니다.
k 변수는 익명 클래스를 사용하여 UnaryOperator 인터페이스를 구현하며, apply() 메서드에서는 정수를 받아들인 후 100을 더한 값을 반환합니다.
k2 변수는 람다 표현식을 사용하여 UnaryOperator 인터페이스를 구현합니다. t를 인자로 받아들인 후 100을 더한 값을 반환합니다.
// IntUnaryOperator
IntUnaryOperator z = new IntUnaryOperator() {
@Override
public int applyAsInt(int x) {
return x+100;
}
};
IntUnaryOperator z2 = t -> t+100;
System.out.println(z2.applyAsInt(100));
IntUnaryOperator를 사용하여 인자로 정수를 받아들이고 100을 더해 정수 타입으로 반환하는 z와 z2 변수가 선언됩니다.
z 변수는 익명 클래스를 사용하여 IntUnaryOperator 인터페이스를 구현합니다. applyAsInt() 메서드에서는 정수를 받아들인 후 100을 더한 값을 반환합니다.
z2 변수는 람다 표현식을 사용하여 IntUnaryOperator 인터페이스를 구현합니다. t를 인자로 받아들인 후 100을 더한 값을 반환합니다.
- 파라미터 있고 리턴 존재(반드시 boolean)
// java.util.function.Predicate 인터페이스 : 파라미터 있고 리턴 존재(반드시 boolean)
/*
Predicate<T> : boolean test(T t)
DoublePredicate : boolean test(double t)
IntPredicate : boolean test(int t)
LongPredicate : boolean test(long t)
BiPredicate<T,U> : boolean test(T t, U u )
*/
Predicate<T>
인터페이스 //Predicate<T>
Predicate<String> x = new Predicate<String>() {
@Override
public boolean test(String t) {
return t.length() == 5;
}
};
// lambda 표현식
Predicate<String> x2 = t -> t.length() == 5;
System.out.println(x2.test("hello"));
//IntPredicate
IntPredicate y = new IntPredicate() {
@Override
public boolean test(int value) {
return value > 10;
}
};
IntPredicate y2 = t -> t > 10;
System.out.println(y2.test(5));
BiPredicate<T, U>
인터페이스//BiPredicate<T, U>
BiPredicate<String, Integer> k = new BiPredicate<String, Integer>() {
@Override
public boolean test(String t, Integer u) {
return t.length() == u;
}
};
BiPredicate<String, Integer> k2 = (t, u) ->t.length() == u;
System.out.println(k2.test("hello", 5));
- 파라미터 없고 리턴 존재
// java.util.function.Supplier 인터페이스 : 파라미터 없고 리턴 존재
/*
Supplier<T> : T get(), T 타입 리턴
BooleanSupplier : boolean getAsBoolean() : boolean 타입 리턴
DoubleSupplier : double getAsDouble() : double 타입 리턴
IntSupplier : int getAsInt() : int 타입 리턴
LongSupplier : long getAsLong() : long 타입 리턴
*/
Supplier<T>
예제 //1. Supplier<T>
// 익명 클래스
Supplier<String> s = new Supplier<String>() {
@Override
public String get() {
return "hello";
}
};
System.out.println(s.get());
// lambda 표현식
Supplier<String> s2 = ()-> "hello2";
System.out.println(s2.get());
IntSupplier 인터페이스
예제 //IntSupplier
// 익명클래스
IntSupplier x = new IntSupplier() {
@Override
public int getAsInt() {
return 100;
}
};
System.out.println(x.getAsInt());
// lambda 표현식
IntSupplier x2 = ()-> 100;
System.out.println(x2.getAsInt());
UnaryOperator<Integer> p3
= Calculator::methodA2;
// 중요UnaryOperator<String> yy3
= String::toUpperCase;
// 중요Consumer<String> m3
= System.out::println;
// 중요package p02;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;
class Calculator{
//static 메서드
public static int methodA(int x , int y) {
return x + y;
}
public static int methodA2(int x) {
return x;
}
// 인스턴스 메서드
public int methodB(int x, int y) {
return x + y;
}
}
public class MethodReference {
public static void main(String[] args) {
//static 메서드
BinaryOperator<Integer> k = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t, Integer u) {
return Calculator.methodA(t, u);
}
};
BinaryOperator<Integer> k2 = (t, u) -> Calculator.methodA(t, u);
BinaryOperator<Integer> k3 = Calculator::methodA;
System.out.println(k.apply(10, 20));
System.out.println(k2.apply(10, 20));
System.out.println(k3.apply(10, 20));
//
Function<String, Integer> z = new Function<String, Integer>() {
@Override
public Integer apply(String t) {
return Integer.parseInt(t);
}
};
Function<String, Integer> z2 = t -> Integer.parseInt(t);
Function<String, Integer> z3 = Integer::parseInt;
System.out.println(z.apply("10") + 10);
System.out.println(z2.apply("10") + 10);
System.out.println(z3.apply("10") + 10);
//UnaryOperator
UnaryOperator<Integer> p = new UnaryOperator<Integer>() {
@Override
public Integer apply(Integer t) {
return Calculator.methodA2(t);
}
};
UnaryOperator<Integer> p2 = t -> Calculator.methodA2(t);
UnaryOperator<Integer> p3 = Calculator::methodA2; // 중요
System.out.println(p.apply(100));
System.out.println(p2.apply(100));
System.out.println(p3.apply(100));
//#######################################
// 인스턴스 메서드
Calculator c = new Calculator();
BinaryOperator<Integer> xx = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t, Integer u) {
return c.methodB(t, u);
}
};
BinaryOperator<Integer> xx2 = (t, u)-> c.methodB(t, u);
BinaryOperator<Integer> xx3 = c::methodB;
System.out.println(xx.apply(10, 20));
System.out.println(xx2.apply(10, 20));
System.out.println(xx3.apply(10, 20));
// String 의 메서드 : 인스턴스 메서드
/*
* String s = "hello";
* s.lenght();
* s.인스턴스메서드();
*
*/
UnaryOperator<String> yy = new UnaryOperator<String>() {
@Override
public String apply(String t) {
return t.toUpperCase();
}
};
UnaryOperator<String> yy2 = t -> t.toUpperCase();
UnaryOperator<String> yy3 = String::toUpperCase; // 중요
System.out.println(yy.apply("hEllO"));
System.out.println(yy2.apply("hEllO"));
System.out.println(yy3.apply("hEllO"));
// System.out.println
Consumer<String> m = new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
};
Consumer<String> m2 = t -> System.out.println(t);
Consumer<String> m3 = System.out::println; // 중요
m.accept("AAA");
m2.accept("AAA");
m3.accept("AAA");
}//end main
}//end class
//1. 배열에서 Stream 얻기
String [] names = {"홍길동","이순신","유관순"};
Stream<String> stream = Arrays.stream(names);
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
};
Consumer<String> consumer2 = t-> System.out.println(t);
Consumer<String> consumer3 = System.out::println;
// stream.forEach(consumer2);
// stream.forEach(t-> System.out.println(t));
// stream.forEach(consumer3);
stream.forEach(System.out::println);
stream.filter(xxx2).forEach(System.out::println);
//2. 컬렉션에서 stream 얻기
List<String> list = Arrays.asList("홍길동1","이순신1","유관순1");
Stream<String> stream2 = list.stream();
stream2.forEach(System.out::println);
package p03;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class StreamTest2 {
public static void main(String[] args) {
// 2. 컬렉션에서 stream 얻기
List<String> list = Arrays.asList("홍길동","이순신", "유관순", "이순신");
Stream<String> stream = list.stream();
// 중간 처리 1 - 중복 제거
stream.distinct().forEach(System.out::println);
// 중간 처리 2 - 필터링
stream = list.stream();
Predicate<String> xxx = new Predicate<String>() {
@Override
public boolean test(String t) {
return t.startsWith("이");
}
};
Predicate<String> xxx2 = t->t.startsWith("이");
System.out.println();
stream.filter(xxx2).forEach(System.out::println); // 중요
}
}
package p03;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamTest3 {
public static void main(String[] args) {
// 리스트(List)를 생성
// 이 리스트에는 1, 2, 3, 4, 5의 정수값이 저장
List<Integer> xyz = Arrays.asList(1,2,3,4,5);
// 최종처리: sum(), count(), max(), min(), average()
// 스트림(Stream)을 생성
// 생성한 스트림을 이용하여 최종처리 메서드인 sum(), count(), max(), min(), average()를 호출
// 이 코드에서는 count() 메서드를 사용하여 리스트(List)의 원소 개수를 구합니다.
Stream<Integer> stream = xyz.stream();
long count = stream.count();
System.out.println("갯수1:" + count);
System.out.println("갯수2:" + xyz.stream().count());
//실습: 짝수값의 갯수 출력하시오.
Stream<Integer> stream2 = xyz.stream();
// 스트림을 이용하여 filter() 메서드를 호출하여 조건에 맞는 값을 추출
//////////////////////////////////////////////////
Predicate<Integer> abc = new Predicate<Integer>() {
@Override
public boolean test(Integer t) {
return t%2==0;
}
};
Predicate<Integer> abc2 = t-> t%2==0;
// 리스트(List)에서 짝수값의 갯수를 구합니다.
System.out.println(stream2.filter(abc2).count());
//////////////////////////////////////////////////
// System.out.println(stream2.filter(t-> t%2==0).count());
///////////////////////////////////////////////////////////
// 스트림의 요소를 int나 double 타입으로 변경하여 처리하는 경우
// 이 때는 mapToInt()나 mapToDouble() 메서드를 사용
List<Integer> xyz2 = Arrays.asList(1,2,3,4,5);
Stream<Integer> kkk2 = xyz2.stream();
// sum()등 집계메서드가 없다. --> IntStream, DoubleStream 등으로 변경 필요
///////////////////////////////////////////////////////////
ToIntFunction<Integer> bbb = new ToIntFunction<Integer>() {
@Override
public int applyAsInt(Integer value) {
return value;
}
};
ToIntFunction<Integer> bbb2 = v->v;
////////////////////////////////////////////////
IntStream kkk3 = kkk2.mapToInt(v -> v);
// IntStream으로 변경된 스트림은 sum() 메서드를 사용하여 총합을 구할 수 있습니다.
System.out.println("총합1:" + kkk3.sum());
// 배열(Array)을 IntStream으로 변경하여 처리하는 경우
// 배열은 바로 IntStream 얻을 수 있다.
int [] ccc = {1,2,3};
IntStream ccc2 = Arrays.stream(ccc);
int sum = ccc2.sum();
System.out.println("총합2:" + sum);
/////////////////////////////////////////
List<Integer> xyz5 = Arrays.asList();
Stream<Integer> kkk5 = xyz5.stream();
IntStream ttt = kkk5.mapToInt(v->v);
OptionalDouble optDouble = ttt.average();
// Optional 타입은 값이 없을경우 예외가 발생되는데, 예외방지가능(기본값설정)
System.out.println("평균값1:" + optDouble.orElse(0.0)); // OptionalDouble[3.0]
double value = optDouble.orElse(0.0); // 권장
// double value2= optDouble.getAsDouble(); //권장안함
System.out.println("평균값2:" + value); // 3.0
/////////////////////////////////////////
List<Integer> xyz6 = Arrays.asList(1,2,3,4,5);
Stream<Integer> kkk6 = xyz6.stream();
IntStream ttt2 = kkk6.mapToInt(v->v);
OptionalInt optInt = ttt2.max(); // ttt2.min()
int value3 = optInt.orElse(0);
System.out.println("최대(소)값:" + value3);
}//end main
}//end class
package p03;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamTest4 {
public static void main(String[] args) {
String [] names = {"홍길동","세종","을지문덕", "이순신"};
/*
* 최종처리: List, Set, Map 반환받기
* .collect(Collectors.toList());
* .collect(Collectors.toSet());
* .collect(Collectors.toMap(key, value));
*/
// 이름이 3글자 이상인 데이터를 List로 반환받기
List<String> list = Arrays.stream(names)
.filter(n->n.length()>2)
.collect(Collectors.toList()); // 이름이 3글자 이상인 데이터를 List로 반환
System.out.println(list); // ["홍길동", "을지문덕", "이순신"]
// 이름 글자수 set로 반환받기 ( 순서없고 중복불가 )
Set<Integer> set = Arrays.stream(names)
.map(n->n.length())
.collect(Collectors.toSet()); // 이름 글자수 set로 반환받기
System.out.println(set); // [2, 3, 4]
// 이름과 글자수를 쌍(Map)으로 반환받기
Map<String, Integer> map = Arrays.stream(names)
.collect(Collectors.toMap(v->v, v->v.length())); // 이름과 글자수를 쌍(Map)으로 반환받기
System.out.println(map); // {홍길동=3, 이순신=3, 을지문덕=4, 세종=2}
}
}