ArrayList 클래스의 forEach 메소드를 보면 Consumer<T>를 매개 값으로 활용하고 있는 것을 알 수 있다
Consumer<T> : T를 받아서 아무 값도 리턴하지 않는 함수형 인터페이스
public class ArrayList<E> extends AbstractList<E>
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
final Object[] es = elementData;
final int size = this.size;
for (int i = 0; modCount == expectedModCount && i < size; i++)
action.accept(elementAt(es, i));
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
따라서 람다를 활용하여 편하게 ArrayList를 출력할 수 있다.
public class Exe {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("B");
names.add("D");
names.add("A");
names.add("C");
names.forEach(name -> System.out.println(name));
}
}
2. Function<T, R>
구현할 추상 메소드 = R apply(T t);
T 타입을 받아서 R을 리턴
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
사용 예시 1
import java.util.function.Function;
public class Test {
public static void main(String[] args) {
// 익명 내부 클래스로 구현
Function<Integer, String> numberToStringInner = new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer.toString();
}
};
// 람다를 활용하여 구현
Function<Integer, String> numberToStringLambda = num -> Integer.toString(num);
System.out.println(numberToStringInner.apply(10) + " : " + numberToStringInner.apply(10).getClass());
System.out.println(numberToStringLambda.apply(10) + " : " + numberToStringLambda.apply(10).getClass());
}
}
람다형식이 더 가독성이 좋다
사용 예시 2
andThen(), compose()를 활용하여 조합해서 사용할 수 있다.
andThen() = 현재 메소드를 실행 후 매개 변수로 받은 함수를 나중에 처리.
compose() = 매개 변수로 받은 함수를 먼저 처리. ( andThen()과 반대 )
import java.util.function.Function;
public class Test {
public static void main(String[] args) {
Function<Integer, Integer> plus10 = num -> num+10;
Function<Integer, Integer> div2 = num -> num/2;
System.out.println("plus10(10) : " + plus10.apply(10));
System.out.println("div2(10) : " + div2.apply(10));
System.out.println("plus10.andThen(div2).apply(10) : " + plus10.andThen(div2).apply(10)); // (10+10)/2
System.out.println("plus10.compose(div2).apply(10) : " + plus10.compose(div2).apply(10)); // (10/2)+10
}
}
3. BiFunction<T, U, R>
구현할 추상 메서드 = R apply(T t, U u);
T, U를 받아서 R타입으로 반환하는 인터페이스
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
사용 예시
import java.util.function.BiFunction;
public class Test {
public static void main(String[] args) {
BiFunction<Integer, Integer, String> addToString = (num1, num2) -> Integer.toString(num1 + num2);
System.out.println(addToString.apply(1,2) + " : " + addToString.apply(1,2).getClass());
}
}
4. UnaryOperator<T>
입력값 하나를 받아서 동일한 타입을 리턴하는 함수 인터페이스
Function<T, R>의 특수한 형태로 Function<T, T>를 상속 받음, 따라서 apply()로 구현
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
사용 예시
import java.util.function.UnaryOperator;
public class Test {
public static void main(String[] args) {
UnaryOperator<Integer> plus50 = (i) -> i + 50;
UnaryOperator<Integer> multiply2 = (i) -> i * 2;
System.out.println(plus50.andThen(multiply2).apply(5));
}
}
5. BinaryOperator<T>
동일한 타입의 입력값 두 개를 받아 리턴하는 함수 인터페이스
BiFunction<T, U, R>의 특수한 형태로 BiFunction<T,T,T>를 상속받음. 따라서 apply()로 구현
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
사용 예시
import java.util.function.BinaryOperator;
public class Test {
public static void main(String[] args) {
BinaryOperator<String> introduce = (name, food) -> "My name is " + name + " I like " + food;
System.out.println("introduce(veneas, chicken) : "+ introduce.apply("veneas", "chicken"));
}
}