6주차 배운점 느낀점 - 람다식, 함수형 인터페이스
6주차 시작
벌써 6주차다 정말빠르다 . 이순간이 좋다 시간이 더가기 싫다.
강사님이 면접에 대해 잠깐 말씀주셨다.
자바에서 메서드 참조의 유형 4가지를 말해보세요 ! -> 뭐뭐뭐뭐가있고 확장성 간결성을 느꼈습니다. -> 어떤걸 작성해보셨는데요??
꼬리의 꼬리를 무는 질문과 대답이 있어야 좋은면접이라고 하셨다.
외울게 너무 많다.
배운점
람다식
코드가 간결해짐
함수형 프로그래밍: 함수를 정의하고 이 함수를 데이터 처리부로 보내 데이터를 처리하는 기법
데이터 처리부는 제공된 함수의 입력값으로 데이터를 넣고 함수에 정의된 처리 내용을 실행
람다식: 데이터 처리부에 제공되는 함수 역할을 하는 매개변수를 가진 중괄호 블록이다.
자바는 람다식을 익명 구현 객체로 변환
'매개변수를 블록안으로 보낸다!'
1.자바 람다는 자바에서 함수형 프로그래밍을 가능하게 하는 자바8 도입된 중요 기능이다.
익명함수인 람다 표현식을 사용하여 메서드에 인수로 전달하거나 변수에 저장하여 데이터를 처리한다.
-parameters(param1, param2,...) - 매개변수 지정(쉼표 구분, 괄호로 묶은 0개 이상의 매개변수)
-lamda operator -> 람다 본문에서 매개변수 리스트를 분리, 함수 시그니처와 본문 사이의 다리 역할
-body{...}
(parameters) -> {body} 람다식이 실행될 코드 묶음
2. 함수형 인터페이스
@FunctionalInterface (선택사항이지만 인터페이스가 SAM 유지하도록 하는데 도움이 됨)
interface MathOperation{int operate(int a, int b; } ==> SAM(Single Abstract Method)
mapToInt: 스트림을 IntStream으로 변환해주는 메소드
Integer 리스트라 바꿔줘야함, sum같은 함수쓰기위해
collect 안쓴이유는 딱히 sum할거라 딱히 다시 list로 만들필요없다.
public class LamdaEx1 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
int sumOfEvenNumbers = numbers.stream()
.filter(number->number % 2 == 0)
.mapToInt(Integer::intValue)
//스트림을 IntStream으로 변환해주는 메소드
.sum();
System.out.println("Sum of even numbers:"+ sumOfEvenNumbers);
}
}
함수형 인터페이스사용하면 원할때마다 구현객체에서 바꿔서 사용할 수 있다.
@FunctionalInterface // 함수형 인터페이스사용
interface MathOperation{
int calculate(int a, int b);
}
public class LamdaEx2 {
public static void main(String[] args) {
MathOperation addition = (a,b) -> a+b;
//원할때마다 바꿔서 사용할 수 있다.
int result = addition.calculate(5,3);
System.out.println("Result : "+result);
}
}
어노테이션 꼭 써줘도 되고 안써줘도됨
밑에 int 배열은 int니까 sum 사용가능 mapToInt필요없음
interface StringOperation {
int getLength(String string);
}
interface ArrayOperation{
int calculateSum(int[] numbers);
}
public class LamdaEx2 {
public static void main(String[] args) {
//문자열의 길이를 반환하는 람다식 작성
StringOperation leng = (string)->string.length();
//람다식 데이터를 전달한 후 반환 처리 구문 작성
int hiLength = leng.getLength("hi");
//결과 출력
System.out.println("length: "+hiLength);
// 정수 배열을 받아 모든 홀수의 합을 반환하는 람다식, 결과 출력 작성
int[] array = {1,2,3,};
ArrayOperation sumArray = (numbers)-> Arrays.stream(numbers).filter(number -> number%2 == 1).sum();
System.out.println("배열 합 "+sumArray.calculateSum(array));
}
}
리스트에서 가장큰 값 찾기
interface FindMax{
int getMax(List<Integer> numbers);
}
---
FindMax findMax = (numbers)->{ //위에있는 findMax
int maxNumber = Integer.MIN_VALUE;
for(int number : numbers){
if(number > maxNumber){
maxNumber = number;
}
}
return maxNumber;
};
List<Integer> numberList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
numberList.add(sc.nextInt());
}
System.out.println(findMax.getMax(numberList));
정수리스트 출력
public class LamdaEx3 {
public static void main(String[] args) {
//1.정수형 리스트를 numbers를 생성하세요. 리스트의 순서는 1,2,3,4,5입니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
//2.numbers의 값을 순서대로 출력하는 람다식을 작성하세요.
numbers.forEach((number) -> { // 하나씪 가져와서 블럭안으로 보낸다.
System.out.println(number);
});
}
}
stream API reduce + 람다식 = 누적 곱 구하기
.reduce : 누적
reduce(1,(a,b)) : 1부터 시작( 1부터 모든 수를 곱하겠다, 더하겠다)
@FunctionalInterface
interface Product{
int calculate(List<Integer> numbers);
}
//---
Product product = products->numbers.stream().reduce(1,(a,b)->a*b);
List<Integer> list = List.of(2,3,4);
System.out.println(product.calculate(list));
이름 리스트를 알파벳 순서로 정렬
Collector.sort 에 new Comparator 사용
Collectors.sort(리스트, new Comparator<String\>(){
public int compare(String a, String b){
return a.compareTo(b);
}
}
==
List<String> names = Arrays.asList("John","Jane","Adam","Eve");
//그냥
Collectors.sort(names,new Comparator<String>(){
public int compare(String a, String b){
return a.compareTo(b);
}
});
//람다
Collections.sort(names.(a,b)->a.compareTo(b));
문자열 리스트 names를 대문자로 변환
map 해주고 collect(Collectors.toList()) 해줌
List<String> upperCaseNames = names.stream()
.map(name -> name.toUpperCase()) // 스트림을 변환시켰기때문에
.collect(Collectors.toList()); // 리스트로 수집
System.out.println(upperCaseNames);
매개변수와 반환값이 없는 람다식
()->{
System.out.println("hello world");
}
타입추론
(name,age)->{ //문맥으로 타입 유추 가능
System.out.println(name);
System.out.println(age);
}
map, filter, reduce, forEach
map : 스트림의 요소를 다른 유형으로 변환하는데 사용
집계함수 : reduce() : 스트림의 요소를 단일 값으로 집계하는 데 사용(축소 연산)
다음에 collect(Collectors.to..);
Optional이란?
자바에서 Null 참조시 NullPointerException을 방지해주는 클래스
Optional에 올 값이 null인 경우 orElse 안에 있는 내용을 실행 시킨다.
그래서 orElse~는 if문을 이용해 처리해야 하는 명령어를 짧게 람다식처럼 처리할 수 있는 메소드라고 볼 수 있다
1. map()
//스트림api 컬렉션에서 다양한 연산을 수행하기 위해 람다 표현식을 허용하는 메서드를 제공한다.
//스트림의 요소를 다른 유형으로 변환하는데 사용
List<Integer> numbers = Arrays.asList(1,2,3,4,5);
List<Integer> squaredNumber = numbers.stream().map(n->n*n).collect(Collectors.toList());
System.out.println(squaredNumber);
2.filter()
//조건에 따라 스트림에서 요소를 선택하는데 사용(조건을 정의하는 람다 표현식을 인수로 받는다)
// 2-1. numbers 대상으로 홀수리스트 oodNumber구성하여 출력
List<Integer> oodNumber1 = numbers.stream().filter(num -> num % 2 == 1).toList();
System.out.println(oodNumber1);
List<Integer> oodNumber2 = numbers.stream().filter(num -> num % 2 == 1).collect(Collectors.toList());
System.out.println(oodNumber2);
// 2-2 nubers 대상으로 짝수 리스트 evenNumber구성하여 출력
List<Integer> evenNumber = numbers.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumber);
3.집계함수 : reduce()
// 스트림의 요소를 단일 값으로 집계하는 데 사용(축소 연산)
int total = numbers.stream().reduce(0, (x, y) -> x + y);
System.out.println("numbers의 총합 집계" + total);
//4. forEach() : 스트림의 요소를 반복하고, 각 요소에 대해 작업을 수행하는데 사용(수행 작업을 하는 람다 표현식을 인수로 받는다)
numbers.stream().forEach(number -> System.out.println(number));
numbers.forEach(number -> System.out.println(number));
응용 문제
// 스트림 API를 사용하여 리스트 numbers의 평균을 구하는 람다식 작성하세요
double average = numbers.stream()
.mapToDouble(Integer::doubleValue)
//double로 형변환
.average()
// 이걸쓰면 optional double이 됨
//Optional이란? 자바에서 Null 참조시 NullPointerException을 방지해주는 클래스
//optional double 이기 때문에 보통 double로 바꿔주는 getAsDouble사용함
.orElse(0.0);
//Optional에 올 값이 null인 경우 orElse 안에 있는 내용을 실행 시킨다.
System.out.println(average);
정렬 sorted 사용, Comparator을 이용
// 가격기준으로
리스트.stream().sorted(Comparator.comparing(리스트::getPrice))
.collect(Collectors.toList());
==
List<Product1> products = Arrays.asList(new Product1("Laptop", 1500),
new Product1("applePhone", 1212),
new Product1("Tablet", 2342),
new Product1("mouse", 654)
);
System.out.println("========Product 리스트=========");
products.forEach(System.out::println);
List<Product1> sortedProducts = products.stream()
.sorted(Comparator.comparing(Product1::getPrice))
.collect(Collectors.toList()); // toList();
System.out.println("========가격별 정렬된 Product 리스트=========");
sortedProducts.forEach(System.out::println);
}
- 자바에서 람다표현식으로 무얼하는거야?
람다표현식은 익명함수를 간결하게 표현하는 방법
람다표현식을 사용하면 메서드에 매개변수로 동작을 전달할 수 있기 때문에 코드의 표현력과 기능성을 향상시킬 수 있다.
자바에서 메서드 참조의 유형 4가지
1.정적 메서드에 대한 참조
클래스명::메서드명 : 람다 표현식이 정적 메서드에 위임할때 유용
// Lambda expression
Function<Integer,String> lambda = (x)->String.valueOf(x);
//Method reference
Function<Integer,String> reference = String::valueOf;
2.특정 개체의 인스턴스 메서드에 대한 참조
object:메서드명
// Lambda expression
Function<String,Integer> lambda = (str)->str.length();
//Method reference
Function<String,Integer> reference = String::length;
3.특정 타입의 임의 객체의 인스턴스 메서드에 대한 참조
ClassName::methodName
// Lambda expression
BiFunction<String,String,Boolean> lambda = (str1,str2)->str1.equals(str2);
//Method reference
BiFunction<String,String,Boolean> reference = String::equals;
4.생성자에 대한 참조
클래스명::new
// Lambda expression
Supplier<List<String>> lambda = ()->new ArrayList<>();
//Method reference
Supplier<List<String>> reference = ArrayList::new;
람다 표현식 예제
public static void main(String[] args) {
//1. 제시되는 list를 오름차순으로 정렬하는 람다식을 작성하세요
List<String> list = Arrays.asList("apple", "banana", "pear", "orange");
list.sort((s1,s2)->s1.compareTo(s2)); // 이거 답
//2. 문자열을 대문자로 변환하는 람다 표현식 작성
Function<String,String> toUppercase = (str)->str.toUpperCase();
Function<String,String> toLowercase = (str)->str.toLowerCase();
System.out.println(toUppercase.apply("hello"));
System.out.println(toLowercase.apply("HELLO"));
//3. 문자열이 비어 있는지 확인하는 람다 표현식 작성
Predicate<String> isEmpty = String::isEmpty;
System.out.println(isEmpty.test(""));
//4. 두 문자열을 연결하는 람다 표현식을 작성
BiFunction<String,String,String> concatename = String::concat;
System.out.println(concatename.apply("자바신세계","개발자과정"));
//5. 숫자의 제곱을 계산하는 람다 식 작성
Function<Integer,Integer> square = (number) -> number * number;
System.out.println(square.apply(5));
}
https://scshim.tistory.com/287
받는 인자 2개는 BiFunction사용
//1. 인수가 없는 생성자 참조(Supplier 함수형 인터페이스는 인수가 없는 객체의 공급자를 나타낸다.)
// 참조된 생성자를 사용하여 새 객체를 생성하는 방법 제공한다.
Supplier<ClassName> constructorRef = ClassName::new;
//2. 인수가 있는 생성자 참조 (인수를 받는 생성자를 나타낸다.)
Function<Integer,ClassName> contructorRef = className::new;
//3. 배열 생성자에 대한 참조
Function<Integer, ClassName[]> constructorRef = ClassName[]::new;
//4. 제너릭 클래스의 생성자 참조
Supplier<GenericClass<Integer>> constructorRef = GenericClass<Integer>::new;
//1. 인수가 없는 String생성자를 사용하여 새 String 객체를 생성하는 람다식 작성
Supplier<String> newString = String::new;
String emptyString = newString.get();
//get메소드를 이용하여 인수가 없는 String 생성자 호출 새로운 빈String 객체 반환
System.out.println(emptyString);
//2. 인수가 없는 ArrayList 생성자를 이용하여 새 ArayList 객체를 생성하는 람다식
Supplier<ArrayList<String>> newArrayList = ArrayList::new;
ArrayList<String> emptyList = newArrayList.get();
System.out.println(emptyList);
//3. 인수가 없는 Random 생성자를 사용하여 새 Random객체를 생성하는 람다식 작성
Supplier<Random> newRandom = Random::new;
Random random = newRandom.get();
int randomNumber = random.nextInt(100);
System.out.println(randomNumber);
퀴즈 내가푼거
//1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int even = list.stream().filter(a -> a % 2 == 0).mapToInt(Integer::intValue).sum();
System.out.println(even);
//2
BiFunction<Integer, Integer, Integer> twoSum = (x, y) -> x + y;
System.out.println(twoSum.apply(1, 2));
BiFunction<Integer,Integer,Integer> pplus = (w,e) -> w+e;
//3
Function<String, Integer> leng = (str) -> str.length();
int len = leng.apply("aaa");
System.out.println(len);
Length length = (str) -> str.length();
System.out.println(length.getLength("aaa"));
//4,5
Function<List<Integer>, Integer> evenSum1 = (arr) -> list.stream()
.filter(a -> a % 2 == 0).mapToInt(Integer::intValue).sum();
System.out.println(evenSum1.apply(list));
//6
Function<List<Integer>,Integer> xxF = (list1) -> list.stream().reduce(1,(x,y)->x+y);
System.out.println(xxF.apply(list));
//7
Function<List<Integer>,Double> averageFun = (list2)->list.stream()
.mapToDouble(Double::valueOf).average().orElse(0.0);
System.out.println(averageFun.apply(list));
//8
MathOperation1 plus = (x,y) -> x+y;
System.out.println(plus.twoSum1(1,2));
//9
Calculator calculator = (a,b)->a+b;
System.out.println( calculator.getSum(1,2));
//10
Function<List<Integer>,Integer> evenSum = (list3)->list3.stream()
.filter(a->a%2==0).reduce(0,(x,y)->x+y);
System.out.println(evenSum.apply(list));
Function<String,Integer> lambda = (str)->str.length();
인수로 Function받기 : 한강이형 코드
메소드에 함수형 인터페이스에서 BiFunction을 인수로 받으면 따로 정의 안해도 똑같이 사용가능하다.
원래는
BiFunction<Double, Double, Double> fun = (x,y)->(x/y);
이 될건데
double 변수 선언하고 = 메서드명( (x,y)->(x/y) ) 으로 표현했다.
public static double calc(BiFunction<Double,Double,Double> fun){
double x= 10;
double y= 4;
return fun.apply(x,y);
}
// BiFunction 으로 인수 받으면 함수형 인터페이스를 따로 정의 안해도 똑같이 이용이 가능하다.
public static void main(String[] args) {
double result = calc((x,y)->(x/y));
// 이렇게 하면 람다식이 Function 인터페이스 메소드 정의를 바로 해버리는 것
System.out.println("result : " + result);
}
회고
람다를 처음 알았을때 -> 하나로 코드가 간결해져서 매력적으로 생각했다.
막상 배워보니 훨씬 깊고 다양한 함수가 존재한다.
확실한건 잘 쓰면 코드가 간결해지고 확장성이 좋을 것같다.
현업에서도 자주쓴다고 하셨다. 연습해야겠다.
점점 하루하루 멘탈이 좀 흔들린다.
내가 안배웠던, 새로보는 내용이다. 수업중에는 강사님이 치시는 내용 따라치고 최대한 말씀을 잘 듣는다.
들은 내용을 바탕으로 수업 후 다시보면 이해가 간다. 다행이다.
처음봐서 흔들리는 맨탈 처음하시는 형들 동생들은 이걸 매일 느꼈을것을 생각하니 대단하다.
매일 새로운 느낌이 이제 아무렇지 않다고 하신다.
나도 이느낌에 익숙해지고 잘 받아들일 줄 알아야한다.