강좌 Course 2. Part 2. ch3 1, 2강 요약
함수형 인터페이스는 단 하나의 추상메서드를 가진 인터페이스를 말한다(=Single Abstract Method, SAM). 자바 8부터 함수형 인터페이스를 사용하여 람다 표현식을 활용할 수 있게 되었으며, @FunctionalInterface 어노테이션을 사용하여 표시한다.
어노테이션은 필수가 아니며, 컴파일러에게 해당 인터페이스가 함수형 인터페이스임을 알려주는 역할을 한다. 어노테이션을 붙이고 추상메서드를 2개 이상 만들면 컴파일러가 빨간 줄을 쳐준다.
일반적으로 인터페이스를 사용하려면, 별도의 외부 클래스에서 인터페이스를 구현 및 재정의를 하고 실행 클래스에서 이 클래스를 불러와 실행을 하게 된다.
// 예시: 인터페이스
@FunctionalInterface
public interface MathOperation {
public int operation(int x, int y); // 추상메서드
}
// 예시: 구현 클래스
import fc.java.c2.model.MathOperation;
public class MathOperationImpl implements MathOperation {
@Override
public int operation(int x, int y) {
return x+y;
}
}
// 예시: 실행 파일
import fc.java.c2.model.MathOperation;
public class FunctionalInterfaceTest2 {
public static void main(String[] args) {
MathOperation mo = new MathOperationImpl();
int result = mo.operation(20,20);
System.out.println("result = "+result);
}
}
하지만 익명 클래스를 사용하면, 실행할 파일에서 인터페이스 객체를 생성하고, 그 자리에서 바로 메서드를 재정의할 수 있다. 즉 별도의 클래스를 만들 필요 없이 메인 클래스 파일에서 바로 인터페이스를 구현하는 것이다.
별도의 클래스가 생성되지 않아 이름이 없기 때문에 익명클래스라고 부른다. 또, 클래스 내부에 있는 클래스기 때문에 익명내부클래스라고 부르는 것이 정확하다.
기존에는 파일이 3개가 필요했다면, 익명 클래스는 2개만 필요하다.
// 예시: 인터페이스
@FunctionalInterface
public interface MathOperation {
public int operation(int x, int y); // 추상메서드
}
// 예시: 실행파일
import fc.java.c2.model.MathOperation;
public class FunctionalInterfaceTest3 {
public static void main(String[] args) {
MathOperation mo = new MathOperation() {
@Override
public int operation(int x, int y) {
return x+y;
}
};
int result = mo.operation(30,40);
System.out.println("result = "+result);
}
}
자바에서 함수형 인터페이스를 사용하는 이유는 다음과 같다.
> 1. 람다 표현식 지원
2. 메서드 참조
3. Stream API와 통합
4. 병렬 프로그래밍
5. 코드 재사용성
이미 정의된 메서드를 참조하면, 기존 메서드를 재사용하여 코드 중복을 줄이고 람다 표현식을 더욱 간결하게 만들 수 있다.
메서드 참조에는 4가지 유형이 있다(콜론 두 개 (::)가 참조 연산자다).
1. 정적 메서드 참조 (클래스명::메서드명)
2. 인스턴스 메서드 참조 (객체참조::메서드명)
3. 특정 객체의 인스턴스 메서드 참조 (클래스명::메서드명)
4. 생성자 참조 (클래스명::new)
Converter라는 함수형 인터페이스를 만들었다.
@FunctionalInterface
public interface Converter<F,T> {
T convert(F from);
}
String을 Integer로 바꿔주는 정적메서드를 가진 IntegerUtils 클래스를 만든다.
public class IntegerUtils {
// static 메서드
public static int stringToInt(String s){
return Integer.parseInt(s);
}
}
Converter를 사용할 때, IntegerUtils의 stringToInt() 정적메서드를 참조하여 사용할 수 있다.
import fc.java.c2.model.Converter;
import fc.java.c2.model.IntegerUtils;
public class IntegerUtilsTest {
public static void main(String[] args) {
// 정적메서드 참조
Converter<String,Integer> converter = IntegerUtils::stringToInt;
int result = converter.convert("999");
System.out.println("result = "+result);
}
}
Converter는 convert 동작을 하면 String을 받아 Integer로 반환하며, 이 동작은 IntegerUtils의 stringToInt를 참조한다.
converter.convert("999")가 실행되면 "999"는 stringToInt로 전달되어 999를 반환된다.
* StringBuffer는 문자열을 추가, 변경할 때 주로 사용하는 자료형이다.
이번에는 String을 받고, 순서를 거꾸로 한 String을 반환하는 StringUtils를 사용해본다.
public class StringUtils {
// 인스턴스메서드 (non-static)
public String reverse(String s){
return new StringBuffer(s).reverse().toString();
}
}
non-static 메서드이므로 StringUtils 인스턴스를 생성한 후 메서드를 참조한다.
import fc.java.c2.model.Converter;
import fc.java.c2.model.StringUtils;
public class StringUtilsTest {
public static void main(String[] args) {
// 인스턴스 생성
StringUtils stringUtils = new StringUtils();
// 인스턴스메서드 참조
Converter<String, String> converter = stringUtils::reverse;
String result = converter.convert("hello");
System.out.println("result = "+result);
}
}
Converter의 convert동작으로 "hello"가 참조된 StringUtils의 reverse 동작으로 넘어가 "olleh"가 반환된다.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SortCompareTest {
public static void main(String[] args) {
// 받은 문자열들을 List로 만들어줌
List<String> names = Arrays.asList("Edgin","Holga","Doric");
// 특정객체메서드 참조(String의 compareTo)
Collections.sort(names, String::compareTo);
System.out.println(names);
}
}
예전에 만들어둔 Person 클래스를 사용할 PersonFactory라는 함수형 인터페이스를 만든다.
// 예시: PersonFactory
@FunctionalInterface
public interface PersonFactory {
public Person create(String name, int age);
}
// 예시: 생성자 메서드 참조
public class PersonFactoryTest {
public static void main(String[] args) {
PersonFactory pf = Person::new;
Person person = pf.create("Tommy",2);
System.out.println(person);