Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[신유승] 3주차 미션 제출합니다. #12

Merged
merged 5 commits into from
Sep 3, 2023
Merged

[신유승] 3주차 미션 제출합니다. #12

merged 5 commits into from
Sep 3, 2023

Conversation

Youthhing
Copy link
Contributor

학습 코드 레포 링크: https://github.com/Youthhing/modern-java

@Youthhing Youthhing changed the title [신유] 3주차 미션 제출합니다. [신유승] 3주차 미션 제출합니다. Aug 22, 2023
Copy link
Member

@euichaan euichaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 유승님!
과제하느라 고생 많으셨습니다!😄
코멘트 남겨주시면서, 람다 예제 부분 코드도 같이 올려주세요!


람다는 함수형 인터페이스에서만 쓰인다. 추상 클래스의 인스턴스를 만들때는 람다를 사용할 수 없다. 또한, 함수형 인터페이스가 아닌 추상 메서드가 여러개인 인터페이스의 인스턴스를 만들때에도 익명 클래스가 사용된다. 또한 람다는 자신을 참조할 수 없어 람다 내부에서 this를 써도 해당 람다가 아닌 람다 외부의 인스턴스를 가리키지만 익명 클래스의 this는 자신을 가르쳐 자기 자신을 참조해야하는 경우에도 익명 클래스를 쓸 수 있다.

또한, 람다와 익명 클래스 모두 직렬화하는 일은 삼가하자!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

람다를 어떻게 직렬화 할 수 있을까요?

직렬화해야 하는 함수 객체가 있다면 private static nested class를 사용하라고 하는데 어떻게 사용할 수 있을까요? (ex: Comparator)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

람다는 익명 함수이기에 다른 인스턴스와 달리 정적인 형태를 갖지 않아 직접적으로 직렬화가 불가능합니다. 따라서, 정적 인터페이스를 클래스 내부에 생성하고 그 안에 람다 로직을 작성할 추상 메서드를 작성해 사용할 수 있습니다.

Copy link
Member

@euichaan euichaan Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization

맨 마지막 줄을 보면 target type(대상 형식, 어떤 콘텍스트에서 기대되는 람다 표현식의 형식)캡쳐된 인수가 직렬화 가능한 경우 람다식을 직렬화 할 수 있다고 합니다. 그러나 익명 클래스와 마찬가지로 람다식의 직렬화는 권장되지 않습니다. strongly discouraged.

https://www.baeldung.com/java-serialize-lambda
위 예제를 살펴보세요.

public static void main(String[] args) {
    // SerializableRunnable obj = () -> System.out.println("please serialize this message");
    SerializableConsumer<String> obj = System.out::println;

    try (FileOutputStream fileOut = new FileOutputStream("serializableConsumer.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
        out.writeObject(obj);
        System.out.println("SerializableConsumer object has been serialized.");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

람다 직렬화를 다음과 같이 수행하면 어떤 예외가 발생하나요?

람다 표현식을 직렬화하지 말아야 하는 이유
람다 표현식이 합성 구문을 생성하기 때문입니다. 그리고 이러한 합성 구문은 소스 코드에 해당 구문이 없거나, 다른 Java 컴파일러 구현 간에 차이가 있거나, 다른 JRE 구현과의 호환성 문제 등 몇 가지 잠재적인 문제를 안고 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 코드를 실행하면 NotSerializableException이 발생합니다. 설명해주신 내용을 바탕으로 이해하면 람다식은 직렬화가 가능하지만, 람다식이 합성구문을 생성해 소스코드에 구문이 없다는등 몇가지의 이유로 인해 권장되지 않다고 이해했습니다! 설명 감사합니다!


스트림은 이전에도 언급했듯 자기 자신을 반환해 여러 메서드를 연속적으로 사용할 수 있다. 연결할 수 있는 스트림 연산을 **중간 연산,** 스트림을 닫는 연산을 **최종 연산**이라고 한다.

**중간 연산**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스트림의 중간 연산에는 어떤 것들이 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스트림의 중간 연산으론 Stream을 반환하는 filter, map, flatmap, distinct, sorted, peek, limit, skip, boxed 등이 있습니다.

Copy link
Member

@euichaan euichaan Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

peek 메서드의 사용법에 대해서 알려주세요. 다음과 같은 코드는 어떻게 동작할까요?

List<String> numbers = Arrays.asList("one", "two", "three", "four", "five",
			"six", "seven", "eight", "nine", "ten");
numbers.stream()
	.filter(s -> s.length() > 3)
	.peek(e -> System.out.println("filter value: " + e))
	.map(String::toUpperCase)
	.peek(e -> System.out.println("map value: " + e))
	.collect(Collectors.toList());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

peek 메서드는 Consumer를 파라미터로 받아 T -> void의 함수형 디스크립터를 가집니다. peek 메서드는 최종연산이 아닌, 중간 연산이기에 최종 연산을 하지 않으면 어떠한 결과도 확인할 수 없습니다. 따라서, peek 연산은 주로 중간에 값을 확인하기 위한 디버깅 용도로 많이 사용됩니다.
위에서 제시해준 코드를 보면, 스트림의 요소가 순차적으로 filter에 의해 글자수 3초과인 String만 걸러지고 , 첫번째 peek을 통해 출력을, map으로 변환후 다시 두번째 peek을 통해 값을 출력합니다. 마지막에 최종연산collect로 List로 변환하는데 이 최종연산이 존재하기에 peek 메서드가 실행되었습니다.

Copy link
Member

@euichaan euichaan Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잘 설명해주셨네요!
https://bugoverdose.github.io/development/stream-lazy-evaluation/
루프 퓨전과 쇼트서킷에 관한 글 공유합니다.


병렬적으로 여러 요소를 연산하며 지속적으로 스트림을 반환한다. 이를 연결해 여러 연산을 할 수 있고 단말 연산을 파이프라인에 실행하기 전까진 아무 연산도 수행하지 않는다는 게으른 특징을 가지고 있다.

**최종 연산**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스트림의 최종 연산에는 어떤 것들이 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스트림의 최종연산은 stream이 아닌 integer, List, void와 같은 반환형을 띄는 foreach, reduce, collect, count,max,min,sum,average, findFirst, findAny와 anyMatch,allMatch,noneMatch등이 있습니다.

Comment on lines +37 to +38
물론, 이렇게 43개의 인터페이스에 존재하지 않거나, 예외를 던져야하는 경우엔 직접 함수형 인터페이스를 작성해야한다. 이때는 Comparator의 쓰임새를 기억하면된다. Comparator가 자주 쓰이는 이유는 이름 자체로 용도를 명확히 설명할 수 있다는 점, 반드시 따라야 하는 규약이 있다는 점, 유용한 디폴트 메서드를 제공할 수 있다는 점이다.

Copy link
Member

@euichaan euichaan Aug 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComparatorComparable의 차이에 대해서 알려주세요.
어떻게 사용하는지 코드로도 보여주세요.

또한, Comparable에는 추상 메서드가 한 개인데, 왜 @FuntionalInterface가 붙어있지 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparable의 추상 메서드는 한 개이지만 이 메서드의 로직은 자기자신과 파라미터를 비교하는 로직입니다. @FunctionalInterface를 사용하게 될 경우, 람다로 사용이 가능한데, 람다에서 this를 호출하면 람다를 호출한 외부 클래스가 반환되므로 구현하는 로직과 달라져@FunctionalInterface가 붙어있지 않습니다.

Copy link
Member

@euichaan euichaan Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크린샷 2023-08-31 오후 12 55 56

이 글을 읽어보면, Comparable의 요점은 비교되는 객체 내부에 이를 구현하는 것이라고 합니다.

https://stackoverflow.com/questions/56827701/is-comparable-interface-a-functionalinterface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하.. 자료를 바탕으로 다시 잘 이해하겠습니다! 설명 감사드립니다!

Copy link
Member

@euichaan euichaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

과제하느라 고생많으셨습니다!💯
전체적으로 코드에 불필요한 빈줄이 많더라고요! 앞으로 형식을 맞추어 코딩하는 것을 권장드립니다.
람다 직렬화에 관련해서, 코멘트 남겼으니 확인해보시고 코멘트 부탁드립니다~!

public static void main(String[] args) {
String emptyString = "";
String nonEmpty ="Hi Java";
Predicate<String> isEmptyString = s -> s.isEmpty();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s -> s.isEmpty() 를 메서드 참조를 이용해 표현해주세요!

public static void main(String[] args) {
List<String> words = Arrays.asList("Ya","Nol","Ja","Ga","Go","Sip","Da");

Collections.sort(words,(s1, s2)->s1.compareTo(s2));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections 의존성을 추가하지 않고도 List의 디폴트 메서드인 sort를 이용해서 해결할 수 있어보입니다.

Comment on lines 11 to 17
List<Integer> integers = Arrays.asList(1,1,2,3,4,4,5,6,6,7,7,7);

List<Integer> result = new ArrayList<>();
integers.stream()
.distinct()
.forEach((i)->result.add(i));
System.out.println(result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrays.asList(1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7)
    .stream()
    .distinct()
    .forEach((s) -> System.out.print(s + " "));

다음과 같이 쓸 수 있을 것 같아요!
forEach 는 앞에서 수행한 연산 결과를 보여주는 일 이상을 하면 안된다고 합니다. (이펙티브 자바 아이템 46)

Comment on lines 12 to 20
Integer max = list.stream()
.max((o1, o2) -> o1.compareTo(o2))
.orElse(9999999);

Integer min = list.stream()
.min((o1, o2) -> o1.compareTo(o2))
.orElse(-99999999);
System.out.println("max = " + max);
System.out.println("min = " + min);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
		
int max = list.stream().reduce(0, Integer::max);
int min = list.stream().reduce(0, Integer::min);
System.out.println("max = " + max);
System.out.println("min = " + min);

reduce를 이용할 수도 있을것같네요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백 주신 내용바탕으로 람다 코드 수정했습니다!


int evenSum = nums.stream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n * n)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본형 특화 스트림을 사용했을 때의 장점을 알려주세요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본형 특화 스트림을 사용할 경우, 언박싱하는 비용이 발생하지 않아 효율적입니다.

@euichaan euichaan merged commit dd0879b into develop Sep 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants