-
java lambda expression / 메서드 참조대학/객체지향프로그래밍 2022. 12. 10. 15:36
- lambda
java를 함수형 언어처럼 사용할 수 있게 하는 문법
(n) -> (n%2) == 0;
메서드와 달리 이름이 없기에 익명 함수라고도 불리고, 메서드와 달리 특정 클래스에 종속되지 않는다.
예외처리도 가능하며 변수에 대입될 수도, 메서드의 인자로 전달될 수도 있다.
interface NumericFunc { int func(int n); } class BlockLambdaDemo { public static void main(String[] args) { NumericFunc factorial = (n) -> { int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; }; System.out.println(factorial.func(3)); // 6 System.out.println(factorial.func(5)); // 120 } }
-> 다음에 오는 값을 리턴하는데, 중괄호가 오게되면 리턴하지 않고, 중괄호 안의 코드를 실행한다.
- functional interface
오직 하나의 추상 메서드를 갖는 인터페이스로 Runnable 인터페이스가 이에 해당한다.
interface NumericTest { boolean test(int n, int d); } class LambdaDemo { public static void main(String[] args) { NumericTest isFactor = (n, d) -> (n % d) == 0; if (isFactor.test(10, 2)) System.out.println("2 is a factor of 10"); if (!isFactor.test(10, 3)) System.out.println("3 is not a factor of 10"); } }
주의할 점은, 람다표현식과 함수형 인터페이스의 추상 메서드는 매개변수와 리턴 값이 일치 해야한다.
interface SomeFunc<T> { T func(T t); } class GenericFunctionalInterfaceDemo { public static void main(String[] args) { SomeFunc<String> reverse = (str) -> { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; }; System.out.println(reverse.func("Lambda")); // adbmaL System.out.println(reverse.func("Expression")); // noisserpxE SomeFunc<Integer> factorial = (n) -> { int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; }; System.out.println(factorial.func(3)); // 6 System.out.println(factorial.func(5)); // 120 } }
람다표현식에는 불가능 하지만, 그 람다표현식을 담을 함수형 인터페이스는 제네릭을 사용할 수 있다.
따라서 하나의 함수형 인터페이스로 여러 타입의 람다표현식을 가리킬 수 있다.
- 람다표현식을 인자로 사용
메서드의 매개변수가 함수형 인터페이스일 때, 그 인자로 람다표현식을 넘길 수 있다.
마치 C에서의 함수 포인터와 같이...
interface StringFunc { String func(String n); } class LambdasAsArgumentsDemo { static String stringOp(StringFunc sf, String s) { return sf.func(s); } public static void main(String[] args) { String inStr = "Lambdas add power to Java"; System.out.println("IN: " + inStr); // IN: Lambdas add power to Java String outStr = stringOp((str) -> str.toUpperCase(), inStr); System.out.println("OUT uppercase: " + outStr); // OUT uppercase: LAMBDAS ADD POWER TO JAVA StringFunc reverse = (str) -> { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; }; System.out.println("OUT reversed: " + stringOp(reverse, inStr)); // OUT reversed: avaJ ot rewop dda sadbmaL } }
- 람다표현식 예외처리
람다표현식 내부에서도 예외를 던질(throw) 수 있다.
하지만, 함수형 인터페이스의 추상 메서드가 던지(throws)는 예외타입과 일치해야 한다.
interface DoubleNumericArrayFunc { double func(double[] n) throws EmptyArrayException; } // 사용자 정의 예외타입 class EmptyArrayException extends Exception { EmptyArrayException() { super("Array Empty"); } } class LambdaExceptionDemo { public static void main(String[] args) throws EmptyArrayException { double[] values = { 1.0, 2.0, 3.0, 4.0 }; DoubleNumericArrayFunc average = (n) -> { double sum = 0; if (n.length == 0) throw new EmptyArrayException(); for (int i = 0; i < n.length; i++) sum += n[i]; return sum / n.length; }; System.out.println(average.func(values)); System.out.println(average.func(new double[0])); } } // 2.5 // Exception in thread "main" EmptyArrayException: Array Empty // at LambdaExceptionDemo.lambda$0(App.java:17) // at LambdaExceptionDemo.main(App.java:23)
- 람다표현식 변수 캡쳐
람다표현식의 내부에서 외부의 변수에 접근할 수 있다.
하지만, 변수에 접근하는 순간, 그 변수는 effectively final이 되기 때문에 앞으로 값을 변경할 수 없게 된다.
interface MyFunc { int func(int n); } class VarCapture { public static void main(String[] args) { int num = 10; MyFunc myLambda = (n) -> { int v = num + n; // num++; // compile error return v; }; // num = 9; // compile error } }
단, class의 멤버 변수를 참조하는 경우에는 변수 캡쳐가 일어나지 않는다.
class의 멤버 함수의 지역 변수에 접근하는 경우에만 변수 캡쳐가 일어난다.
- 메서드 참조
람다표현식을 인자로 넘길 수 있지만, 메서드 자체를 인자로 넘길 수도 있다. (메서드 참조)
interface StringFunc { String func(String n); } class MyStringOps { static String strReverse(String str) { String result = ""; int i; for (i = str.length() - 1; i >= 0; i--) result += str.charAt(i); return result; } } class MethodRefDemo { static String stringOp(StringFunc sf, String s) { return sf.func(s); } public static void main(String[] args) { String inStr = "Lambdas add power to Java"; String outStr = stringOp(MyStringOps::strReverse, inStr); System.out.println("Original string: " + inStr); System.out.println("String reversed: " + outStr); } }
위와 같이 static 메서드를 참조할 때는 ClassName::methodName 문법을 사용하지만,
객체의 메서드를 참조할 때는 objName::methodName 문법을 사용한다.
interface MyFunc<T> { int func(T[] vals, T v); } class MyArrayOps { static <T> int countMatching(T[] vals, T v) { int count = 0; for (int i = 0; i < vals.length; i++) if (vals[i] == v) count++; return count; } } class GenericMethodRefDemo { static <T> int myOp(MyFunc<T> f, T[] vals, T v) { return f.func(vals, v); } public static void main(String[] args) { Integer[] vals = { 1, 2, 3, 4, 2, 3, 4, 4, 5 }; String[] strs = { "One", "Two", "Three", "Two" }; int count; count = myOp(MyArrayOps::<Integer>countMatching, vals, 4); System.out.println(count); // 3 count = myOp(MyArrayOps::<String>countMatching, strs, "Two"); System.out.println(count); // 2 } }
제네릭 역시 사용할 수 있다.
interface MyFunc { MyClass func(int n); } class MyClass { private int val; MyClass(int v) { val = v; } int getVal() { return val; }; } class ConstructorRefDemo { public static void main(String[] args) { MyFunc myClassCons = MyClass::new; MyClass mc = myClassCons.func(100); System.out.println("val in mc is " + mc.getVal()); } }
className::new 문법으로 생성자 함수도 참조할 수 있다.
단, func의 리턴 타입이 MyClass가 되어야 한다.
interface MyFunc<T> { MyClass<T> func(T n); } class MyClass<T> { private T val; MyClass(T v) { val = v; } T getVal() { return val; }; } class ConstructorRefDemo2 { public static void main(String[] args) { MyFunc<Integer> myClassConsInt = MyClass<Integer>::new; MyClass<Integer> mcInt = myClassConsInt.func(100); System.out.println("val in mcInt is " + mcInt.getVal()); MyFunc<String> myClassConsStr = MyClass<String>::new; MyClass<String> mcStr = myClassConsStr.func("Generic!"); System.out.println("val in mcStr is " + mcStr.getVal()); } }
제네릭 역시 사용할 수 있다.
'대학 > 객체지향프로그래밍' 카테고리의 다른 글
java collection framework (0) 2022.12.10 java 네트워크 프로그래밍 (0) 2022.12.10 java generic(2) (0) 2022.12.10 java generic(1) (0) 2022.12.09 java transient / volatile / instanceof / native / assert (0) 2022.12.09