2024. 1. 28. 21:40ㆍ공부 중/Java
1. Lamda 표현식
public class SamTest {
SamTest(){
Sam s = new Sam();
InterImpl impl = new InterImpl();
s.m1(impl);
}
public static void main(String[] args) {
new SamTest();
}
}
public class Sam {
void m1 (Inter inter) {
int res = inter.calc(1, 2);
System.out.println(res);
}
}
public class InterImpl implements Inter {
@Override
public int calc(int a, int b) {
return a+b;
}
}
public interface Inter {
int calc(int a, int b);
}
고작 Inter.calc(1, 2)
를 한 번 실행하기 위해서 Inter
인터페이스를 구현하는 InterImpl
클래스도 정의해야 하고 InterImpl impl
객체도 생성해야 하고 이를 s.m1()
으로 전달까지 해야 한다.
결국 전달하고 싶은 것은 구체적으로 정의된Inter.calc()
메서드다.
이때 불필요한 코드를 획기적으로 줄일 수 있는 방법이 람다식이다.
가. Anonymous inner class
InterImpl
처럼 단 한 번만 사용하는데 .java
파일을 만드는 것이 아까울 때?
public class SamTest {
class InterImpl implements Inter {
@Override
public int calc(int a, int b) {
return a+b;
}
}
SamTest(){
Sam s = new Sam();
InterImpl impl = new InterImpl();
s.m1(impl);
}
public static void main(String[] args) {
new SamTest();
}
}
InterImpl
를 SamTest
내부로 이동.
Inner class 또는 nested class라고 한다.
Inter impl = new InterImpl();
class InterImpl implements Inter {
@Override
public int calc(int a, int b) {
return a+b;
}
}
InterImpl
은 Inter
로 참조할 수 있다.
InterImpl
은 어차피 한 번만 사용되는데 굳이 InterImpl
class를 정의하는 것이 비효율적인 것 같다.
Inter impl = new Inter() {
@Override
public int calc(int a, int b) {
return a+b;
}
};
Inter()
는 사실new InterImpl();
+class InterImpl implements Inter
와 같다.
단, 이때는 ;
를 더 이상 생략할 수 없다.
이런 클래스를 anonymous inner class라고 한다.
그런데 impl
도 한 번만 사용되니깐 이것도 귀찮나 보다.
SamTest(){
Sam s = new Sam();
s.m1(new Inter() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
}
그런데 이것도 길다고 생각했나 보다. 그래서 람다가 탄생했다.
나. 람다식
@FunctionalInterface
public interface Inter {
int calc(int a, int b);
}
@FuntinalInterface
: 인터페이스 안에 추상메서드는 단 1개만 존재해야 함.
: 조건만 따른다면 어노테이션이 있든 없든 상관없음.
public class Sam {
void m1 (Inter inter) {
int res = inter.calc(1, 2);
System.out.println(res);
}
}
이미 컴파일 단계에서 매개변수의 타입과 @FunctionalInterface
를 만족하는 경우 접근할 수 있는 메서드가 하나밖에 없다는 사실을 알 수 있다.
이 사실을 참고해 불필요한 부분을 다 줄인다.
// before
SamTest(){
Sam s = new Sam();
s.m1(new Inter() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
}
// after
SamTest(){
Sam s = new Sam();
s.m1((int a, int b)->{
return a+b;
});
}
→
: 람다 기호new Inter() { @Override public int calc
생략.
그런데 매개변수의 타입도 알 수 있어서 줄일 수 있다.
SamTest(){
Sam s = new Sam();
s.m1((a, b)->{return a+b;});
}
그런데 여기서도 실행문이 한 줄이면 {}과 return
을 생략할 수 있다.
s.m1((a, b)-> a+b);
그런데 만약 전달받는 매개변수가 하나라면 ()
도 생략할 수 있다.
//before
s.m1((a)-> a);
//after
s.m1(a -> a);
다. lambda 블록에서의 변수 참조
- lambda 블록 밖 클래스 member 변수는 접근제한자의 제약 없이 사용가능함.
- lambda 블록 밖 클래스의 local 변수는 final 키워드가 추가된 것을 동작 → read-only
라. java.util.function
Inter
와 같이 @FuntinalInterface
를 만족하는 인터페이스들은 어차피 하는 일이 비슷하다.
java.util.function
에 자주 사용되는 함수적 표준 interface를 정의해 두었다.
parameter | return | method | 비고 | |
java.lang.Runnable | X | X | void run() | Thread 용도 |
Consumer 계열 | O | X | void accept(T) | 소비자 |
Supplier 계열 | X | O | T get() | 공급자 |
Funtion 계열 | O | O | T apply (R) | 주로 parameter를 이용한 연산 후 원하는 타입으로 return : 새로운 타입으로 변환 |
Operation 계열 | O | O | T XXX(T) | 주로 parameter를 연산해서 return : 동일한 타입으로 연산 결과를 반환 |
Predicate 계열 | O | boolean | boolean test(T) | parameter를 조사해서 return 결정 |
- 예시
//consumer 계열
Consumer<String> consumer = x -> System.out.println(x);
consumer.accept("Hello");
//supplier 계열
IntSupplier supplier = () -> {
Random random = new Random();
return random.nextInt(g));
};
System.out.println(supplier.getAsInt());
//function 계열
ToIntBiFunction<String, String> function = (src1, scr2) -> {
return Integer.parseInt(src1) + Integer.parseInt(src2);
}
System.out.println(function.applyAsInt("4", "5"));
//Operator 계열
UnaryOperator<Double> operator = (x) -> {return Math.pow(x,2);}; //제곱
System.out.println(operator.apply(10.0));
//Predicate 계열
Predicate<String> predicate = (name) -> {return name.contains("Java");};
System.out.println(predicate.test("JavaScript"));
마. 메서드 참조
Method reference
1) Parameter method 참조
Integer[] nums = {1,7,3,4,5};
Arrays.sort(nums, (num1, num2) -> num1.compareTo(num2)); // instance method
Arrays.sort(nums, Integer::compareTo);
2) Class method 참조
Integer[] nums = {1,7,3,4,5};
Arrays.sort(nums, (num1, num2) -> Integer.compare(num1, num2)); // static method
Arrays.sort(nums, Integer::compare);
3) instance method 참조
List<String> names = new ArrayList<>(Arrays.asList("Hello", "Java", "World"));
names.forEach(item -> names.add(item));
names.forEach(names::add);
names.forEach(item -> System.out.println(item));
names.forEach(System.out::println);
4) 생성자 참조
Supplier<StringBuffer> s1 = () -> new StringBuffer();
Supplier<StringBuffer> s1 = StringBuffer::new;
Function<String, StringBuffer> f1 = (init) -> new StringBuffer();
Supplier<String, StringBuffer> f1 = StringBuffer::new;
파라미터의 개수와 타입에 따라서 알맞은 생성자 호출.
모두 다 외우진 못해도 보고 이해할 수 있어야 한다.
궁금하면 Method reference 찾아보자.
2. Arrays.sort()
Java에서 제공하는 배열을 관리하는 유틸리티.
import java.util.Arrays;
public class ATest {
public ATest() {
int[] arr = {1,5,8,2,4};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new ATest();
}
}
Arrays.sort(arr);
: arr 배열을 오름차순으로 정렬.Arrays.toString(arr);
: arr 배열을 문자열로 변환.
가. 배열을 Comparator로 정렬하기
Comparable
을 구현하지 않은 타입을 정렬하고 싶거나 원하는 방식으로 정렬하기 위해선 별도의 Comparator
를 구현해야 한다.
Comparator
를 직접 구현해서 정렬하는 연습을 해보자.
public static <T> void sort(T[] a, Comparator<? super T> c)
의 T
는 Class type
이 들어가야 한다. (int[]
와 같은 배열도 들어가긴 한다. Reference type이면 가능한 건가?)
그래서 int
의 Wrapper class인 Integer
로 배열을 만들어야 한다.
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, null); // Comparator가 아직 없음.
class MyComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
// Integer -> int 자동으로 형변환 // Auto unboxing
return o1 - o2; // 오름
//return -(o1 - o2); // 내림
}
}
Comparator
를 구현.
return o1 - o2;
: Integer -> int로 auto unboxing
: 오름차순return -(o1 - o2);
: 내림차순
하지만 o1 - o2
는 오버플로우가 발생할 수 있다.
System.out.println(Integer.MAX_VALUE - Integer.MIN_VALUE);
→ -1
Comparator myComparator = new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
Integer.compare(o1, o2)
를 사용하자.
public class BTest {
MyComparator myComparator = new MyComparator();
class MyComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
}
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, myComparator);
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
/*
[1, 3, 4, 7, 8, 9]
*/
나. 람다식으로 만들기
public class BTest {
Comparator myComparator = new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, myComparator);
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
MyComparator
를 anonymous inner class로 만든다.
사실 여기까지는 대부분의 ide에서 자동완성을 지원해 줌.
public class BTest {
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
});
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
어차피 사용할 수 있는 메서드가 하나라서 new Comparator<Integer>() { @Override public int compare
를 생략할 수 있다.
public class BTest {
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, (Integer o1, Integer o2)->{
return Integer.compare(o1, o2);
});
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
Integer
와 { };
을 생략할 수 있다.
public class BTest {
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, (o1, o2)-> Integer.compare(o1, o2));
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
Integer.compare()
는 static method라서 Class method 참조가 가능하다.
메서드 참조가 발생해서 반복되는 (o1, o2)
생략한다.
public class BTest {
BTest(){
Integer[] arr = {3,1,7,9,4,8};
Arrays.sort(arr, Integer::compare);
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
new BTest();
}
}
'공부 중 > Java' 카테고리의 다른 글
[Java] File IO (0) | 2024.01.29 |
---|---|
[Java] Collection (0) | 2024.01.28 |
[Java] 예외 처리 (0) | 2024.01.28 |
[Java] String (0) | 2024.01.22 |
[Java] 인터페이스 (0) | 2024.01.22 |