/////
Search
Duplicate
3️⃣

자바 함수

자바의 함수는 정적 메서드와 같은 의미의 수학적인 함수처럼 사용되며 부작용을 일으키지 않는 함수를 의미한다.
자바 8에서는 함수를 새로운 값의 형식으로 추가했다. 즉 함수를 값처럼 취급하는 것이다.
먼저 자바 프로그램에서 조작할 수 있는 값을 생각해보자.
int, double 형식의 기본값, String namename, 즉 객체의 참조도 값이다. 배열도 객체의 참조로서 값으로 넘겨줄 수 있다.
그런데 왜 함수가 필요할까? 프로그래밍 언어의 핵심은 값을 바꾸는 것이다.
전통적으로 프로그래밍 언어에서는 이 값으로 전달될 수 있는 이러한 것들을 일급 객체라고 부른다.
이렇게 값을 전달할 수 없는 구조체는 2급 객체다. 위에서 언급한 값은 모두 일급 객체지만 메서드, 클래스 등은 2급 객체에 해당한다.
인스턴스화한 결과가 값으로 귀결되는 클래스를 정의할 때 메서드를 아주 유용하게 활용할 수 있지만 그 자체로 값이 될 수 없다.
런타임에 메서드를 전달할 수 있다면 즉, 메서드를 일급 객체로 만든다면 프로그래밍에 유용하게 사용할 수 있다.

1. 메서드와 람다를 일급 클래스로

메서드 참조 :: 를 사용해서 메서드를 값으로 직접 전달할 수 있다.
디렉터리에서 모든 숨겨진 파일을 필터링하는 코드
File[] hiddenFiles = new File(".").listFiles(new FileFilter() { public boolean accept(File file) { return file.isHidden(); // 숨겨진 파일 필터링 } });
Java
복사
자바 8에서는 아래처럼 구현할 수 있다.
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
Java
복사
자바 8에서는 더 이상 메서드가 2급 객체가 아닌 일급 객체라는 것이다.
기존에 객체 참조를 이용해서 객체를 이리저리 주고 받았던 것처럼 자바 8에선 File::isHidden을 이용해서 메서드 참조를 만들어 전달할 수 있다.
람다: 익명 함수
자바 8에서는 메서드뿐만아니라 람다를 포함한 함수들도 값으로 취급할 수 있다.
(int x) → x + 1, 즉 x라는 인수로 호출하면 x + 1을 반환하는 동작을 수행하도록 코드를 구현할 수 있다.
MyMathUtils라는 클래스를 만든 다음 클래스 내부에 add1이라는 메서드를 정의해서 Utils::add1을 만들 수 있으므로 굳이 필요한가? 싶을 수 있다.
메서드를 직접 정의할 수도 있지만 이용할 수 있는 편리한 클래스나 메서드가 없을 때 새로운 람다 문법을 이용하면 굳이 정의하지 않고도 더 간결하게 코드를 구현할 수 있다.
람다 문법 형식으로 구현된 프로그램을 함수형 프로그래밍, 즉 함수를 일급값으로 넘겨주는 프로그램을 구현한다라고 한다.

2. 코드 넘겨주기 : 예제

Apple 클래스와 getColor 메서드가 있고 Apples 리스트를 포함하는 변수 inventory가 있다고 가정하자.
이때 모든 녹색 사과를 선택해서 리스트를 반환하는 프로그램을 구현하고자 한다. 이처럼 특정 항목을 선택해서 반환하는 동작을 filter라고 한다.
자바 8 이전이라면 filterGreenApple이라는 메서드를 구현했을 것이다.
public static List<Apple> filterGreenApples(List <Apple> inventory) { List<Apple> result = new ArrayList<>(); for (Apple apple: inventory) { if (GREEN.equals(apple.getColor())) { result.add(apple); } } return result; }
Java
복사
만약 무게가 150g 이상인 것만 필터링하고 싶다면 다음과 같이 또 구현했을 것이다.
public static List<Apple> filterHeavyApples(List<Apple> inventory) { List<Apple> result = new ArrayList<>(); for(Apple apple : inventory) { if(apple.getWeight() > 150) { result.add(apple); } } return result; }
Java
복사
소프트웨어 공학적인 측면에서 복붙의 단점은 복붙한 코드에 버그가 있다면 복붙한 모든 코드를 고쳐야한다는 것이다.
이 예제에서 두 메서드는 단순히 조건문만 다르고 모든 부분이 같다.
자바 8에서는 코드를 인수로 넘겨줄 수 있으므로 filter 메서드를 중복으로 구현할 필요가 없다.
public static boolean isGreenApple(Apple apple) { return GREEN.equals(apple.getColor()); } public static boolean isHeavyApple(Apple apple) { return apple.getWeight() > 150; } // 명확히 하기위해 적어놓음 // 보통은 java.util.function에서 임포트함 public interface Predicate<T> { boolean test(T t); } // 메서드가 p라는 이름의 프레디케이트 파라미터로 전달됨 static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p { List<Apple> result = new ArrayList<>(); for(Apple apple : inventory) { if(p.test(apple)) { result.add(apple); } } return result; } // 아래처럼 메서드를 호출할 수 있다. filterApples(inventory, Apple::isGreenApple); filterApples(inventory, Apple::isHeavyApple);
Java
복사

3. 메서드 전달에서 람다로

하지만 한 두 번만 사용할 메서드를 매번 정의하는 것은 귀찮은 일이다. 자바 8에서는 이러한 문제를 간단하게 해결할 수 있다.
filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor())); filterApples(inventory, (Apple a) -> a.getWeight() > 150); filterApples(inventory, (Apple a) -> a.getWeight() < 80 || RED.equals(a.getColor()) );
Java
복사
위 코드처럼 한 번만 사용할 메서드는 굳이 정의할 필요 없이 익명 람다를 사용해서 전달해줘도 무방하다.
만약 람다가 몇 줄 이상으로 길어진다면 익명 람다보다는 코드가 하는 일을 잘 설명하는 이름을 가진 메서드를 정의하고 메서드 참조를 활용하는 것이 바람직하다.