[자바]/자바

[Java] 함수형 인터페이스

쿠릉쿠릉 쾅쾅 2022. 2. 3. 01:24
728x90

 

 

▒ 함수형 인터페이스

 

 

 

🔍 Predicate<T> 함수형 인터페이스

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
  • 전달된 인자를 대상으로 true, false를 판단할 때 쓰인다.
더보기
더보기
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public  class Prac {	

    public static void main (String[] args) {    

    	List<Integer> list = Arrays.asList(1, 5, 7, 9, 11, 12);
    	int s;
    	s = sum(n -> n%2 ==0, list);
    	System.out.println("짝수 합 : " + s); 
    	
    	s = sum(n -> n%2 !=0, list);
    	System.out.println("홀수 합 : " + s);
    	
    }
    
    public static int sum(Predicate<Integer> p, List<Integer> lst) {
    	int s = 0;
    	for(int n : lst) {
    		if(p.test(n))
    			s += n;
    	}
    	return s;
    }
    
}

 

짝수 합 : 12
홀수 합 : 33

 

💡 Predicate<T> 함수형 인터페이스 종류

✔ IntPredicate 함수형 인터페이스

@FunctionalInterface
public interface IntPredicate {
    boolean test(int value);
}

 

 

✔ LongPredicate 함수형 인터페이스

@FunctionalInterface
public interface LongPredicate {
    boolean test(Long value);
}

 

 

✔ DoublePredicate 함수형 인터페이스

@FunctionalInterface
public interface DoublePredicate {
    boolean test(double value);
}

 

 

✔ BiPredicate<T, U> 함수형 인터페이스

@FunctionalInterface
public interface BiPredicate<T, U> {
    boolean test(T t, U u);
}
  • 함수형 인터페이스 이름 앞에 'Bi' 가 붙은 경우에는 매개변수의 개수가 2개다.

 

 

 

 

🔍 Supplier<T> 함수형 인터페이스

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • 단순히 다엇인가 반환할 때 쓰인다.
더보기
더보기
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;

public  class Prac {	

    public static void main (String[] args) {    

    	Supplier<Integer> spr = () -> {
    		Random rand = new Random();
    		return rand.nextInt(50);
    	};
    	
    	List<Integer> list = makeIntList(spr, 5);
    	System.out.println(list);
    	
    	list = makeIntList(spr, 10);
    	System.out.println(list);
    	
    }
    
    public static List<Integer> makeIntList(Supplier<Integer> s, int n) {
    	List<Integer> list = new ArrayList<>();
    	
    	for(int i=0; i<n; i++)
    		list.add(s.get());
    	
    	return list;
    }
    
}
[37, 40, 27, 6, 14]
[5, 35, 3, 41, 35, 25, 0, 39, 9, 22]
  • 난수를 리스트의 요소로 넣기 때문에 실행마다 리스트의 요소 값이 달라진다.

 

💡 Supplier<T> 함수형 인터페이스 종류

✔ IntSupplier 함수형 인터페이스

@FunctionalInterface
public interface IntSupplier {
    int getAsInt();
}

 

 

✔ LongSupplier 함수형 인터페이스

@FunctionalInterface
public interface LongSupplier {
    long getAsLong();
}

 

 

✔ DoubleSupplier 함수형 인터페이스

@FunctionalInterface
public interface DoubleSupplier {
    double getAsDouble();
}

 

 

✔ BooleanSupplier 함수형 인터페이스

@FunctionalInterface
public interface BooleanSupplier {
    boolean getAsBoolean();
}

 

 

 

 

🔍 Consumer<T> 함수형 인터페이스

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  • 전달된 인자를 가지고 어떤 결과(반환 제외)를 보여야 할 때 사용한다. 
더보기
더보기
import java.util.function.Consumer;

public  class Prac {	

    public static void main (String[] args) {    

    	Consumer<String> c = s -> System.out.println(s);
    	c.accept("Pineapple");
    	c.accept("Strawberry");
    	
    }
    
}
Pineapple
Strawberry
  • Consumer<T> 인터페이스를 통해 출력이라는 결과를 구현했다.

 

💡 Consumer<T> 함수형 인터페이스 종류

✔  IntConsumer 함수형 인터페이스

@FunctionalInterface
public interface IntConsumer {
    void accept(int value);
}

 

 

✔ ObjIntConsumer<T> 함수형 인터페이스

@FunctionalInterface
public interface ObjIntConsumer<T> {
    void accept(T t, int value);
}
  • obj타입 매개변수와 int 타입 매개 변수를 받는다.
더보기
더보기
import java.util.function.ObjIntConsumer;

public  class Prac {	

    public static void main (String[] args) {    

    	ObjIntConsumer<String> c = (s, i) -> System.out.println(i + ". " + s);
    	
    	int n = 1;
    	
    	c.accept("Toy", n++);
    	c.accept("Book", n++);
    	c.accept("Candy", n);
    }
    
}
1. Toy
2. Book
3. Candy

 

 

✔ LongConsumer 함수형 인터페이스

@FunctionalInterface
public interface LongConsumer {
    void accept(long value);
}

 

 

✔ ObjLongConsumer<T> 함수형 인터페이스

@FunctionalInterface
public interface ObjLongConsumer<T> {
    void accept(T t, long value);
}
  • obj타입 매개변수와 long 타입 매개 변수를 받는다.

 

 

✔ DoubleConsumer 함수형 인터페이스

@FunctionalInterface
public interface DoubleConsumer {
    void accept(double value);
}

 

 

✔ ObjDoubleConsumer<T> 함수형 인터페이스

@FunctionalInterface
public interface ObjDoubleConsumer<T> {
    void accept(T t, double value);
}
  • obj타입 매개변수와 double 타입 매개 변수를 받는다.

 

 

✔ BiConsumer<T, U> 함수형 인터페이스

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

 

 

 

 

🔍 Function<T, R> 함수형 인터페이스

@FunctionalInterface
public interface function<T, R> {
    R apply(T t);
}
  • 전달 인자와 반환 값이 존재할 때 쓰인다. 
더보기
더보기
import java.util.function.Function;

public  class Prac {	

    public static void main (String[] args) {    

    	Function<String, Integer> f = s -> s.length();
    	System.out.println(f.apply("Robot"));
    	System.out.println(f.apply("System"));
    }
    
}
5
6

 

💡 Function<T, R> 함수형 인터페이스 종류

  • 반환형과 매개변수형이 동일한 인터페이스는 이름에 Operator로 끝나는 규칙이 있다.
  • 매개변수가 하나면 인터페이스 이름 앞에 Unary가 붙는다.

 

 

✔ IntToDobuleFunction 함수형 인터페이스

@FunctionalInterface
public interface IntToDoubleFunction {
    double applyAsDouble(int value);
}
  • int타입 → double타입 형변환

 

 

✔ DoubleToIntFunction 함수형 인터페이스

@FunctionalInterface
public interface DoubleToIntFunction {
    int applyAsInt(double value);
}
  • double 타입 → int 타입 형변환

 

 

✔ IntUnaryOperator 함수형 인터페이스 

@FunctionalInterface
public interface IntUnaryOperator {
    int applyAsInt(int operand);
}
  • 매개변수와 반환값이 모두 int 타입이다.

 

 

✔ DoubleUnaryOperator 함수형 인터페이스

@FunctionalInterface
public interface DoubleUnaryOperator {
    double applyAsDouble(double operand);
}
  • 매개변수와 반환값이 모두 double 타입이다.
더보기
더보기
import java.util.function.DoubleUnaryOperator;

public  class Prac {	
    public static void main (String[] args) {    
    	DoubleUnaryOperator cti = d-> d * 0.393701;
    	DoubleUnaryOperator itc = d-> d * 2.54;
    	System.out.println("1 cm = " + cti.applyAsDouble(1.0) + "inch");
    	System.out.println("1 inch = " + itc.applyAsDouble(1.0) + "cm");
    }
    
}
1 cm = 0.393701inch
1 inch = 2.54cm

 

 

✔ BiFunction<T, U, R> 함수형 인터페이스

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}
  • 매개변수를 2개 받고 반환값이 1개다.

 

 

✔ IntFunction<R> 함수형 인터페이스

@FunctionalInterface
public interface IntFunction<R> {
    R apply(int value);
}
  • 매개변수 타입이 int타입이다.

 

 

✔ DoubleFunction<R> 함수형 인터페이스

@FunctionalInterface
public interface DoubleFunction<R> {
    R apply(double value);
}
  • 매개변수 타입이 double타입이다.

 

 

✔ ToIntFunction<T> 함수형 인터페이스

@FunctionalInterface
public interface ToIntFunction<T> {
    int applyAsInt(T value);
}
  • 반환 타입이 int타입이다.
더보기
더보기
import java.util.function.ToIntFunction;

public  class Prac {	

    public static void main (String[] args) {    

    	ToIntFunction<String> f = s -> s.length();
    	System.out.println(f.applyAsInt("Robot"));
    	System.out.println(f.applyAsInt("System"));
    }
    
}
5
6

 

 

✔ ToDoubleFunction<T> 함수형 인터페이스

@FunctionalInterface
public interface ToDoubleFunction<T> {
    double applyAsDouble(T value);
}
  • 반환 타입이 double타입이다.

 

 

✔ ToIntBiFunction<T, U> 함수형 인터페이스

@FunctionalInterface
public interface ToIntBiFunction<T, U> {
    int applyAsInt(T t, U u);
}
  • 매개변수가 2개고 반환타입이 int 타입이다.

 

 

✔ ToDoubleBiFunction<T, U> 함수형 인터페이스

@FunctionalInterface
public interface ToDoubleBiFunction<T, U> {
    double applyAsDouble(T t, U u);
}
  • 매개변수가 2개고 반환타입이 double 타입이다.

 

 

✔ UnaryOperator<T> 함수형 인터페이스

@FunctionalInterface
public interface UnaryOperator<T> {
    T apply(T t);
}
  • Function<T, R> 함수형 인터페이스를 상속 받았다.
  • Function<T, R> 함수형 인터페이스에서 모든 타입인자인 T, R을 일치시켜서 타입 인자를 한 개만 받는다.

 

 

✔ BinaryOperator<T> 함수형 인터페이스

@FunctionalInterface
public interface BinaryOperator<T> {
    T apply(T t1, T t2);
}
  • BiFunction<T, U, R> 함수형 인터페이스를 상속 받았다.
  • BiFunction<T, U, R> 함수형 인터페이스에서 모든 타입 인자인 T, U, R을 일치시켜서 타입 인자를 한 개만 받는다.

 

 

 

 

 

▒ 함수형 인터페이스의 합성과 결합

 

🔍 andThen() 메서드

  • Consumer, Function, Operator 종류의 함수형 인터페이스의 디폴트 메서드다.

 

더보기
더보기

예제1

import java.util.function.Function;

public  class Prac {	
    public static void main (String[] args) { 
    	Function<String, Integer> f = s -> Integer.parseInt(s, 16);
    	Function<Integer, String> g = i -> Integer.toBinaryString(i);
    	
    	Function<String, String> h = f.andThen(g);
    	System.out.println(h.apply("FF"));  // "FF" → 255 → "11111111"
    	
    }
    
}
11111111
  • f.andThen(g)는 함수 f를 먼저 적용하고, 그 다음에 함수 g를 적용한다. ( f → g )

 


 

예제2

import java.util.function.Consumer;

public  class Prac {	
    public static void main (String[] args) { 
    	Consumer<Member> consumerA = m -> System.out.println("consumerA: " + m.getName());
    	
    	Consumer<Member> consumerB = m -> System.out.println("consumerB: " + m.getId());
    	
    	Consumer<Member> consumerAB = consumerA.andThen(consumerB);
    	consumerAB.accept(new Member("홍길동", "hong", null));
    }
}

class Member {
	private String name;
	private String id;
	private Address address;
	
	public Member(String name, String id, Address address) {
		this.name = name;
		this.id = id;
		this.address = address;
	}
	
	public String getName() {return name;}
	public String getId() {return id;}
	public Address getAddress() {return address;}
	
}


class Address {
	private String country;
	private String city;
	
	public Address(String country, String city) {
		this.country = country;
		this.city = city;
	}
	
	public String getCountry() {return country;}
	public String getCity() {return city;}
	
}
consumerA: 홍길동
consumerB: hong
  • Consumer 함수형 인터페이스는 반환값이 없기 때문에 andThen() 메서드는 함수형 인터페이스의 호출 순서만 정한다.

 


 

예제3

import java.util.function.Function;

public  class Prac {	
    public static void main (String[] args) { 
    	Function<Member, Address> functionA;
    	Function<Address, String> functionB;
    	Function<Member, String> functionAB;
    	String city;
    	
    	functionA = m -> m.getAddress();
    	functionB = a -> a.getCity();
    	
    	functionAB = functionA.andThen(functionB);
    	city = functionAB.apply(new Member("홍길동", "hong", new Address("한국", "서울")));
    	
    	System.out.println(city);
    	
    }
}

class Member {
	private String name;
	private String id;
	private Address address;
	
	public Member(String name, String id, Address address) {
		this.name = name;
		this.id = id;
		this.address = address;
	}
	
	public String getName() {return name;}
	public String getId() {return id;}
	public Address getAddress() {return address;}
	
}


class Address {
	private String country;
	private String city;
	
	public Address(String country, String city) {
		this.country = country;
		this.city = city;
	}
	
	public String getCountry() {return country;}
	public String getCity() {return city;}
	
}
서울
  • Function<Member, Address> + Function<Address, String> = Function<Member, String>

 

 

 

🔍 compose() 메서드

  • Function, Operator 종류의 함수형 인터페이스의 디폴트 메서드다.

 

더보기
더보기

예제1

import java.util.function.Function;

public  class Prac {	
    public static void main (String[] args) { 
    	Function<String, Integer> f = s -> Integer.parseInt(s, 16);
    	Function<Integer, String> g = i -> Integer.toBinaryString(i);
    	
    	Function<Integer, Integer> h = f.compose(g);
    	System.out.println(h.apply(2));  // 2 → "10" → 16
    	
    }
    
}
16
  •  f.compose(g)는 함수 g를 먼저 적용하고 함수 f를 적용한다. ( g → f )

 

 


 

import java.util.function.Function;

public  class Prac {	
    public static void main (String[] args) { 
    	Function<Member, Address> functionA;
    	Function<Address, String> functionB;
    	Function<Member, String> functionAB;
    	String city;
    	
    	functionA = m -> m.getAddress();
    	functionB = a -> a.getCity();
    	
    	functionAB = functionB.compose(functionA);
    	city = functionAB.apply(new Member("홍길동", "hong", new Address("한국", "서울")));
    	
    	System.out.println(city);
    	
    }
}

class Member {
	private String name;
	private String id;
	private Address address;
	
	public Member(String name, String id, Address address) {
		this.name = name;
		this.id = id;
		this.address = address;
	}
	
	public String getName() {return name;}
	public String getId() {return id;}
	public Address getAddress() {return address;}
	
}


class Address {
	private String country;
	private String city;
	
	public Address(String country, String city) {
		this.country = country;
		this.city = city;
	}
	
	public String getCountry() {return country;}
	public String getCity() {return city;}
	
}
서울
  • Function<Member, Address> + Function<Address, String> = Function<Member, String>

 

 

💡 정리

종류 함수형 인터페이스 andThen() compose()
Consumer Consumer<T> O  
BiConsumer<T, U> O  
DoubleConsumer O  
IntConsumer O  
LongConsumer O  
Function Function<T, R> O O
BiFunction<T, U, R> O  
Operator BinaryOperator<T> O  
DoubleUnaryOperator O O
IntUnaryOperator O O
LongUnaryOperator O O

 

 

 

🔍 Predicate의 결합

  • 여러 Predicate를 디폴트 메서드인 and(), or(), negate()로 연결해서 하나의 새로운 Predicate로 결합할 수 있다.
  • isEqula() 메서드는 static 메서드다.
    • 두 대상을 비교하는 Predicate를 만들 때 사용한다.
    • isEqual() 메서드는 매개변수에 null 값도 받을 수 있다.
    • isEqual() 메서드의 매개변수로 비교 대상을 하나 지정하고, 또 다른 비교 대상은 test()의 매개변수로 지정한다.
함수형 인터페이스 and() or() negate() isEqula()
Predicate<T> O O O O
BiPredicate<T, U> O O O  
DoublePredicate O O O  
IntPredicate O O O  
LongPredicate O O O  

 

더보기
더보기

예제1

import java.util.function.Predicate;

public  class Prac {	
    public static void main (String[] args) { 
    	Predicate<Integer> p = i -> i<100;
    	Predicate<Integer> q = i -> i<200;
    	Predicate<Integer> r = i -> i%2==0;
    	Predicate<Integer> notP = p.negate();  // i >= 100;
    	
    	// 100 <= i && (i < 200 || i%2 ==0)
    	Predicate<Integer> all = notP.and(q.or(r));
    	System.out.println(all.test(150));
    	
    	// negate()를 마지막에 붙이면 조건식 전체가 부정이 된다.
    	Predicate<Integer> notAll = notP.and(q.or(r)).negate();
    	System.out.println(notAll.test(150));
    
    	String str1 = "abc";
    	String str2 = "abc";
    	
    	Predicate<String> p2 = Predicate.isEqual(str1);
    	boolean result = p2.test(str2);
    	System.out.println(result);
    	
    	boolean result2 = Predicate.isEqual("hello").test("hello");
    	System.out.println(result2);
    	
    }
    
}
true
false
true
true

 


 

예제2

import java.util.function.IntPredicate;

public  class Prac {	
    public static void main (String[] args) {
    	
    	// 2의 배수 검사
    	IntPredicate predicateA = a -> a%2==0;
    	
    	// 3의 배수 검사
    	IntPredicate predicateB = a -> a%3==0;
    	
    	IntPredicate predicateAB;
    	boolean result;
    	
    	// and()
    	predicateAB = predicateA.and(predicateB);
    	result = predicateAB.test(9);
    	System.out.println("9는 2와 3의 배수입니까? " + result);
    	
    	
    	// or()
    	predicateAB = predicateA.or(predicateB);
    	result = predicateAB.test(9);
    	System.out.println("9는 2 또는 3의 배수입니까? " + result);
    	
    	// negate()
    	predicateAB = predicateA.negate();
    	result = predicateAB.test(9);
    	System.out.println("9는 홀수입니까? " + result);
    	
    	
    }
    
}
9는 2와 3의 배수입니까? false
9는 2 또는 3의 배수입니까? true
9는 홀수입니까? true

 

 

 

 

🔍 minBy(), maxBy() 메서드

  • BinaryOperator<T> 함수형 인터페이스는 static 메서드인 minBy(), maxBy()를 제공한다.
  • 두 메소드는 매개변수값으로 제공되는 Comparator를 이용하여 최대 T와 최소 T를 얻어서 BinaryOperator<T> 타입으로 반환한다.
리턴 타입 메서드
BinaryOperator<T> minBy(Comparator<? super T> comparator)
maxBy(Comparator comparator)

 

더보기
더보기
import java.util.function.BinaryOperator;

public  class Prac {	
    public static void main (String[] args) { 
    	
    	BinaryOperator<Fruit> bo;
    	Fruit fruit;

    	bo = BinaryOperator.minBy((f1, f2) -> f2.price - f1.price);
    	fruit = bo.apply(new Fruit("딸기", 6000), new Fruit("수박", 10000));
    	System.out.println(fruit.name);
    	
    	bo = BinaryOperator.maxBy((f1, f2) -> f2.price - f1.price);
    	fruit = bo.apply(new Fruit("딸기", 6000), new Fruit("수박", 10000));
    	System.out.println(fruit.name);
    	
    }
    
}

class Fruit {
	int price;
	String name;
	
	Fruit(String name, int price) {
		this.name = name;
		this.price = price;
	}
	
}
수박
딸기

 

 

 

 

 

 

▒ 컬렉션 프레임워크와 함수형 인터페이스

더보기

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public  class Prac {	
    public static void main (String[] args) { 
    	List<Integer> list = new ArrayList<>();
    	
    	for(int i=0; i<10; i++) list.add(i);
    	
    	// list의 모든 요소 출력
    	list.forEach(i -> System.out.print(i + ","));
    	System.out.println();
    	
    	// list에서 2의 배수 또는 3의 배수 제거
    	list.removeIf(x-> x%2==0 || x%3==0);
    	System.out.println(list);
    	
    	// list의 각 요소에 10을 곱한다.
    	list.replaceAll(i -> i*10);
    	System.out.println(list);
    	
    	Map<String, String> map = new HashMap<>();
    	map.put("1", "11");
    	map.put("2", "22");
    	map.put("3", "33");
    	map.put("4", "44");
    	
    	// map의 모든 요소를 {k, v}의 형식으로 출력
    	map.forEach((k, v) -> System.out.print("{" + k + ", " + v + "}, "));
    	
    }
    
}​
0,1,2,3,4,5,6,7,8,9,
[1, 5, 7]
[10, 50, 70]
{1, 11}, {2, 22}, {3, 33}, {4, 44},

 

 

 

728x90