Coder Social home page Coder Social logo

javs's Introduction

2023 Java Study(Javs)

Java Study Cover

🗓 Plans

2023.02.17(금) ~ (매주 금요일 19:00)

진행 기간 모임 일정
모던 자바 인 액션 23.02.17 - 23.05.18 매 주 금요일 19:00
이팩티브 자바 23.05.18 - 24.01.04 매 주 목요일 21:00

🤝 Study Purpose

선택한 책의 빠른 1회독.

책을 읽으며 어떤 상황에 어떻게 사용할 것인지의 기준 정립.

책을 읽고 주도적으로 더 알아본 부분을 서로 공유하며, 지식의 선순환을 도모.

📌 Issue

질문은 5~15분 정도 토론할 수 있는 것으로 준비한다.

가급적 서술형으로 답변할 수 있는 주제를 선정한다.

이미 먼저 등록된 이슈들과 서로 겹치지 않는 문제를 선정하여 이슈로 등록한다.

토론 목적의 문제 이외에도 자유롭게 책의 주제와 관련된 문제들을 등록할 수 있다.

세부 형식은 다음의 내용을 따른다.

이슈 제목 형식 : 문제 제목(이름)

이슈 라벨 : ex) 이펙티브 자바, item52

이슈 내용 형식

  1. 문제가 무엇인가?
  2. 왜 이러한 문제를 선정하였는가
  3. 자신이 생각하는 답변은 무엇인가

🚀 Goals

모던 자바 인 액션 빠른 1회독을 목표로 한다.

🙋🏻‍♂️ Participants

스터디원 Github 프로필
영규 pyg410
지민 jminkkk
수민 kssumin
도연 capDoYeonLee
서현 blackbean99
종민 rlajm1203
다애 dongE

javs's People

Contributors

pyg410 avatar blackbean99 avatar capdoyeonlee avatar kssumin avatar rlajm1203 avatar

Stargazers

윤석호 avatar Minju Kwak avatar  avatar MayHyeyeonKim avatar Sonny avatar  avatar  avatar Moly avatar

Watchers

James Cloos avatar  avatar

javs's Issues

열거타입을 확장가능하게 하는 코드 예제가 이해가 안되네요(박영규)

문제가 무엇인가?

p.234에 보면 인터페이스를 통한 열거타입에 확장성을 부여하는 코드가 있는데,

private static <T extends Enum<T> & Operation> void test(
  Class<T> opEnumType, double x, double y) {
  for (Operation op : opEnumType.getEnumConstants())
    System.out.printf("%f %s %f = %f%n",
                      x, op, y, op.apply(x, y));
}

<T extends Enum<T> & Operation>이 이해가 안되네요.

왜 이런 문제를 선정하였는가?

제 생각이 맞는지도 궁금하고,

다른 분들은 어떻게 생각했는지 궁금합니다.

자신이 생각한 답변은 무엇인가?

이해안되는 부분은 다음과 같습니다.

  1. 열거타입 확장은 자바에서 금지된 것으로 알고 있는데 어떻게 Enum과 같이 확장할 수 있는가?
  2. T extends Enum 으로만 해도 괜찮을 것 같은데 왜 굳이 Generic을 통해 T extends Enum<T> 와 같이 작성했는가?

첫번 째의 경우 Enum타입은 모든 열거 타입의 common base class입니다.

자바에서는 각 열거타입, Color라는 열거타입이 존재할 때, 해당 Color클래스는 Enum를 상속받고 있다고 판단합니다.

앞서 확장 불가능한 타입은 Color 열거타입이지, Enum가 아니라고 하네요.

자바의 모든 클래스가 Object를 상속받지 않더라도 컴파일 단계에서 자동으로 extends Object를 하는 것과 같은 상황이라고 생각하면 좋을 것 같습니다.

두번 째의 경우 컴파일되는 과정과 연관되어 있다고 생각합니다.

열거타입은 컴파일 될때 애초에 public final class Color extends Enum<Color>과 같이 컴파일 됩니다.

이는 Enum내에 있는 compareTo 메서드도 포함되는데, 당연하게도 compareTo메서드는 같은 타입끼리만 비교해야하므로 Color타입만을 인수로 받아야합니다.

해당 타입만을 인수로 받게 하려면 Enum이 Generic해야하며, Enum의 타입 파라미터인 T를 인수로 받아야합니다.

그렇기 때문에 Enum의 타입 파라미터를 인수로 받아야 하는 것이라고 생각합니다.

타입 안전 열거패턴이란?(박영규)

문제가 무엇인가?

타입 안전 열거 패턴이라는 말이 나왔는데, 잘 모르겠습니다.

왜 이런 문제를 선정하였는가?

개념 이슈..

자신이 생각한 답변은 무엇인가?

Type safe enumeration pattern은 기존에 public static final 필드로 정수 타입 열거 패턴으로 작성하던 코드에 다음과 같은 문제점들이 있어 생긴 pattern이라고 합니다.

  1. 새로운 상수를 추가 했을 경우 사용자 코드도 다시 컴파일 해야한다.
  2. 새로운 상수가 사용자 코드와 충돌을 일으킬 수 있다.
  3. etc..
    문자열로 변경하더라도 타이핑 오류를 확인할 수 없어 디버깅이 어려워지기 때문에 정수던 문자열이던 좋지 않은 방법입니다.

이로 인해서 Type Safe Enummeration Pattern이 생겼는데요.

간단한 예시는 다음과 같습니다.
스크린샷 2023-09-04 오후 3 30 26

이러한 타입 안전 열거 패턴의 장점으로는,

  1. static final 필드에서 정의된 객체 외에 다른 객체가 생성될 수 없다.
  2. 컴파일 시점의 타입 안정성을 제공한다.
  3. 새로운 상수가 추가되더라도 client코드를 변경할 필요가 없다.
  4. 상수를 출력가능한 문자열로 바꿀 수 있다.

개인적으로는 3번 장점으로 인해 OCP준수가 가능해진다는 점이 제일 마음에 드는 것 같습니다.

Optional은 왜 null로 비어있다.를 표현할까?(김수민)

문제가 무엇인가?

Optional null로 비어있는 것인지 확인한다.라고 합니다.
굳이 Optional은 왜! null로 비어있는지 확인하는 것일까요?

왜 이러한 문제를 선정하였는가

왜 근데 굳이 null이지 라는 생각을 하게 되었습니다.

자신이 생각하는 답변은 무엇인가

Optional 클래스는 타입 매개변수 T를 가지는 제네릭 클래스입니다.

public final class Optional<T>{
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
}

따라서 Optional 클래스는 참조 타입(wrapper class)의 경우에만 사용되며, 값이 없는 경우(null)를 포함하는 Wrapper 클래스입니다. Primitive 타입은 기본적으로 null 값을 가질 수 없으며, 값이 없으면 자동으로 초기값이 설정됩니다. 따라서 Primitive 타입의 경우에는 Optional 클래스를 사용할 필요가 없고, null 여부를 확인할 필요도 없습니다.

null , NullPointerException

null

참조 변수 타입은 힙 영역의 객체를 참조하지 않는다는 뜻으로 null 값을 가질 수 있다.
오직 null은 참조 타입 변수만 가질 수 있고, null 값으로 초기화 할 수 있다.

NullpointerException

자바에서는 null과 관련된 예외가 존재한다.
NullPointerException이라는 예외는 참조 변수가 null값을 가지는데, 해당 참조 변수를 사용하려고 할 때 발생한다.

NoSuchElementException

Optional은 null을 감쌀 수 있다.(즉 NullPointerException 예외가 발생하는 걸 방지하기 위해 사용한다.)
따라서 Optional에 값이 없는데 값을 꺼내려고 한다면(get())이때는 NosuchElementException이 발생한다.

와일드 카드에대해 알아봅시다(박영규)

문제가 무엇인가?

p.208
와일드카드 "?"에 대해서 기억이 잘 나지 않는다.

왜 이러한 문제를 선정하였는가

와일드카드에 대해 복습하고자 이슈로 선정했다.

자신이 생각하는 답변은 무엇인가

제네릭이 등장했지만 오히려 실용성이 떨어지는 상황들이 생기면서, 모든 타입을 대신할 수 있는 와일드카드 타입()을 추가하였다. 와일드카드는 정해지지 않은 unknown type이기 때문에 Collection로 선언함으로써 모든 타입에 대해 호출이 가능해졌다. 그래서 제네릭의 활용성을 높일 수 있게 되었는데, 여기서 중요한 것은 와일드카드가 any type이 아닌 unknown type이라는 점이다.

unknown타입이기 때문에 어떤타입인지 알 수 없다. 즉, 범위가 무제한이다.

@Test
void genericTest() {
    Collection<?> c = new ArrayList<String>();
    c.add(new Object()); // 컴파일 에러
}

다음과 같은 코드에 컴파일 에러가 뜨는 이유이기도 하다.

JAVA는 위와같은 문제를 해결하고자 와일드카드에 상한범위와 하한범위를 지정할 수 있게 했다.

  1. 상한 경계 와일드 카드
void printCollection(Collection<? extends MyParent> c)
{ ... }
  1. 하한 경계 와일드 카드
void addElement(Collection<? super MyParent> c) 
{ ... }

그렇다면 와일드 카드는 언제 써야할까?
이는 추후 이펙티브 자바를 공부하다보면 PECS라는 공식이 등장하는데 이건 그때 배우도록 하자.

참고

익명클래스와 지역클래스의 차이점은 무엇일까? (박영규)

문제가 무엇인가?

익명클래스라는 말을 처음 들어보았다. 책에 나와있는 설명은 이해하기 부족했다.
책에서는 익명클래스와 지역클래스가 비슷한 개념이라고 하는데, 익명클래스와 지역클래스의 차이점이 무엇일까?

익명클래스는 자바의 지역클래스와 비슷한 개념이다. 익명 클래스는 말 그대로 이름이 없는 클래스다...
즉석에서 필요한 구현을 만들어서 사용할 수 있다.

왜 이러한 문제를 선정하였는가

익명클래스와 지역클래스의 차이점을 알아보면, 두 클래스의 개념을 명확히할 수 있을 것 같다.

자신이 생각하는 답변은 무엇인가

지역클래스(Local Class)란?

  • 로컬 클래스라는 말은 지역변수 처럼 쓰인다.
  • 메소드 안에서 정의된다.
  • default 이거나 final 또는 abstarct 로 만 선언 가능한것
  • 로컬클래스 도 로컬 변수라고 생각하면 쉽다.
  • 지역클래스를 포함하는 메소드 안에서만 객체를 생성 할수 있다.
  • 메소드의 실행이 끝나면 해당 지역 클래스가 메모리에서 사라지게 된다.

참고

익명클래스란?

  • 클래스의 선언과 객체의 생성을 동시에 할 수 있는 문법
  1. 다른 클래스를 상속하거나, 인터페이스 1개를 구현한 클래스만 선언할 수 있다.
    new 부모클래스명() { /**/ } : 부모클래스를 상속한 익명 클래스를 선언하면서 객체를 1개 생성하여 반환.
    new 인터페이스명() { /**/ } : 인터페이스를 구현한 익명 클래스를 선언하면서 객체를 1개 생성하여 반환.
    즉, 다른 클래스 혹은 인터페이스 없이는 익명 클래스 선언불가.
    또한 복수의 인터페이스를 구현하거나, 클래스 상속과 인터페이스 구현을 동시에 수행하는 등의 작업은 불가.

  2. 내부 클래스의 일종이다.
    자바에서 익명 클래스는 별도의 클래스 내부에서만 사용될 수 있기 때문에 당연하다.
    내부 클래스는 "인스턴스 클래스", "스태틱 클래스", "지역 클래스", "익명 클래스"로 총 4가지다.
    컴파일 시점에 각 익명클래스마다 외부클래스명$숫자.class 형식의 클래스 파일이 생성된다.

  3. 일반적인 클래스는 컴파일 시점에 클래스명.class 형식의 클래스 파일이 생성된다.
    일반적인 내부 클래스는 본인이 들어있는 외부 클래스와 자신의 클래스명이 합쳐진 외부클래스명$내부클래스명.class 형식의 파일이 생성된다.
    익명 클래스는 말 그대로 이름이 없는 클래스이기 때문에 클래스명 대신 생성된 순서대로 1,2,3과 같은 숫자가 붙여진다.

참고 : 자바의 정석

정적 팩터리 메서드가 생성자보다 좋은 다섯 가지(이도연)

9p 두 번째, 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.

왜 이러한 문제를 선정하였는가?

저는 이게 큰 장점이라고 보기 힘들다고 생각했어요. 정적 팩터리 메서드에서 생성자를 private으로 제한해서 새로운 객체 생성을 제한하고 getInstance()메서드를 static으로 선언해서 인스턴스를 생성하도록 하죠. 그럼 singleton disign pattern을 구현할 수 있어요. 그런데 의존성 주입을 생성자를 통해 구현을 했을 경우 singleton과 유사한 효과를 볼 수 있어요. 제가 생각하는 의존성 주입은 두 객체간의 결합도를 낮추기 위함, 추상화, 테스트(유지보수)용이 라고 생각합니다.

자신이 생각하는 답변은 무엇인가!

디폴트 메서드를 재정의 할 때는 override 어노테이션이 강제가 아닌 선택인 이유?(김수민)

문제가 무엇인가?

public interface A {
    default void hello(){
        System.out.println("Hello from A");
    }
}

A 인터페이스가 위와 같을 경우 B 인터페이스의 디폴트 메서드인 hello는 @override선택이다

public interface B extends A{

    @Override
    default void hello(){
        System.out.println("Hello from B");
    }
}
public interface B extends A{

    default void hello(){
        System.out.println("Hello from B");
    }
}

B인터페이스 결국은 A 인터페이스의 디폴트 메서드 구현을 오버라이딩 한 게 아닌가? 근데 왜 @override어노테이션을 사용하지 않아도 되는거지?

왜 이러한 문제를 선정하였는가

인터페이스의 디폴트 메서드는 메서드의 구현부분을 가지고 있습니다.
이후 이 인터페이스를 사용하는 클래스에서 디폴트 메서드의 내용을 재정의하고 싶으면 다시 이 디폴트 메서드의 구현 부분을 바꾸는데
이때 컴파일러에게 이거는 오버라이딩 한 부분이야! 라는 걸 알려주기위해서 @override 를 사용해야하지 않나? 라는 생각이 들었습니다.

그래서 테스트를 해본 결과
디폴트 메서드를 가지고 있는 인터페이스를 사용하는 클래스에서 이 디폴트 메서드를 재정의할 때 @Override를 사용해도 되고 안 해도 컴파일 에러가 발생하지 않았습니다.(물론 런타임 에러도 발생하지 않음)

따라서 왜 디폴트 메서드를 재정의할 때 @Override 를 사용하지 않아도 되는지 궁금합니다.

자신이 생각하는 답변은 무엇인가

디폴트 메서드가 구현이 되어있는 메서드이기는 하나 디폴트 메서드가 기본적으로 인터페이스에서 정의된 메서드입니다.
즉, 하위 클래스에서 해당 메서드를 구현하게 되면, 인터페이스에서 정의한 메서드를 오버라이딩 하는 것이 되고, 이는 @Override어노테이션을 사용하지 않아도 됩니다.

하지만!!! 클래스 상속을 통해 메서드를 재정의 할 경우에는 @Override을 사용하여 재정의된 메서드임을 컴파일러에게 알려주어야 한다.

즉, 디폴트 메서드라서 @Override를 사용하지 않아도 되는 것이 아니라
디폴트 메서드는 기본적으로 인터페이스의 메서드이기 때문에 디폴트 메서드를 재정의하더라도 인터페이스 메서드를 구현한 것과 같습니다.

한정적 메서드 참조와 비한정적 메서드 참조(박영규)

문제가 무엇인가?

아이템 43의 단어가 어려워서 정확하게 이해하고 넘어가고 싶습니다.

왜 이런 문제를 선정하였는가?

비한정적 메서드 참조와 한정적 메서드 참조가 정확히 어떤 차이가 있는지 생각해보고 공유하고 싶습니다.

자신이 생각한 답변은 무엇인가?

한정적(bound) 인스턴스 메서드 참조

  • 수신객체(참조 대상 인스턴스)를 특정하는 참조이며, 즉 함수 객체가 받는 인수와 참조되는 메서드가 받는 인수가 같다.
Instant.now()::isAfter
Instant then = Instant.now();
t -> then.isAfter(t)

비한정적(unbound) 인스턴스 메서드 참조

  • 수신객체를 특정하지 않는 참조이며, 함수 객체를 적용하는 시점에 수신 객체를 알려준다.
  • 주로 스트림 파이프라인에서의 매핑과 필터 함수에 쓰인다.
String::toLowerCase
str -> str.toLowerCase()

즉, 한정적 인스턴스 메서드 참조는, 메서드 참조를 할 때 결과(인수)를 받는 객체가 정해져 있는 경우이고,

비한정적 인스턴스 메서드 참조는, 메서드 참조를 할 때 결과(인수)를 받는 객체가 런타임에 정해지는 경우이다.

동작 파리미터화 코드 전달하기(김종준)

문제가 무엇인가?

요구사항은 항상 변경될 수 있고 우리는 이를 대비하여야 하는데 이를 어떻게 대비할 수 있을까요?

왜 이러한 문제를 선정하였는가

위의 문제로 설명이 될 것 같습니다.

자신이 생각하는 답변은 무엇인가

이를 대비하는 방법이 인터페이스(ex ApplePredicate)를 통해 요구사항을 어떻게 수행할지 틀만 정의하고 구체적인 코드(ex AppleColorPredicate)는 파라미터로 전달받는 것이 있고

이때 파라미터로 전달받는 방법은 인터페이스를 정의한 클래스, 익명 클래스, 람다 표현 식과 같은 방법이 있습니다.

EnumMap을 생성할 때 Key 정보를 전달해야 하는 이유(김수민)

문제가 무엇인가?

p.228 EnumMap의 생성자가 받는 키 타입의 Class 객체는 한정적 타입 토큰으로, 런타임 제네릭 타입 정보를 제공한다.

Map<Plant.LifeCycle, Set<Plant>> plantsByLIfeCycle = new EnumMap<>(Plant.LifeCycle.class);

왜 이런 문제를 선정하였는가?

왜 EnumMap을 생성할 때는 Key 타입을 전달해주어야 하는거지?
왜 추론을 못 하는 걸까?

자신이 생각한 답변은 무엇인가?

Why EnumMap constructor needs class argument?

핵심 -> 제네릭은 컴파일 타임의 기능이다.
따라서 런타임 시에는 타입 소거에 의해서 타입을 알 수가 없다.
하지만 런타임시에 타입 정보가 필요할 수도 있다.
plantsByLifeCycle에 새로운 객체를 넣는다거나 할 때.....
하지만 타입 소거에 의해서 타입 정보를 알 수 없기 때문에 Key의 Class객체를 전달해서 런타임에서 Map에 객체를 추가할 수 있다.

그렇다면 HashMap은 왜 타입 정보를 전달하지 않아도 되는거지?
이는 EnumMap 같은 경우는 Enum인 key값 자체를 전달받아서 해당 key를 통해 ordinal메서드를 이용해 인덱스 값을 얻는다.
하지만, HashMap 같은 경우는 해당 key값이 어떤 class인지가 아니라 해당 key값을 해시함수를 돌려 그에 해당하는 해시값으로 인덱스를 설정한다.

이렇듯, 내부적으로 HashMap, EnumMap이 전달받은 Key를 통해 index를 설정하는 방법이 다르기 때문에

EnumMap은 HashMap과 달리 생성시에 key타입의 클래스를 전달해야 한다.

Optional을 왜 사용해야할까?(박영규)

문제가 무엇인가?

p.168 Optional클래스는 값의 존재나 부재 여부를 표현하는 컨테이너 클래스다. null은 쉽게 에러를 일으킬 수 있으므로 자바8 라이브러리 설계자는 Optional를 만들었다.

Optional은 NullPointerException을 방지하기 위해 사용한다고 한다. 그럼 Optional.of는 null인 경우 NPE를 발생하는데 왜 사용할까?

왜 이러한 문제를 선정하였는가

이전 프로젝트에 Optional을 왜 사용하는지도 모르고 사용방법만 숙지한채로 사용했는데, 문득 Optional을 보니 왜 사용하는지 궁금해졌다.

자신이 생각하는 답변은 무엇인가

Optional의 효과는

  1. 명시적으로 해당 변수가 null일 수도 있다는 가능성을 표현할 수 있음
  2. NPE를 유발할 수 있는 null을 직접 다루지 않아도 됨
  3. null체크를 직접 하지 않아도 됨. 가독성이 좋아진다.

내 생각에는 3번이 가장 중요한 이유이지 않을까 싶다.
다만, 객체로 한번 감싼다는 것은 그만큼 비용이 더 든다는 의미니까, 10장을 공부할 때 Optional을 어느 상황에 써야하는지 확실히 숙지해야할 것 같다.

스트림이 안좋은 상황은 무엇이 있을까?(박영규)

문제가 무엇인가?

스트림이 과연 항상 좋지는 않을 것 같다.
스트림의 성능이 안좋은 상황은 무엇이 있을까?

왜 이러한 문제를 선정하였는가

스트림을 언제 사용해야하는지, 사용하면 안되는지 알아두면 모두에게 좋을 것 같다.

자신이 생각하는 답변은 무엇인가

스트림은 항상 for문보다 빠르지 않다.
JVM이 for문의 최적화를 오래했기 때문에 최적화가 더 잘된다고 한다.
단, 순회비용이 큰 경우에는 for문의 최적화가 무색해질 수 있다.
ArrayList의 경우에는 원시형 타입인 int와 달리 stack이 아닌 heap에 저장되기 때문에 간접참조를 해서 순회비용이 크다.
즉, for문과 스트림의 격차가 적다.
결론 : for문보다 스트림을 써야하는 경우는 소스 컬렉션이 충분히 큰 경우 혹은 컴퓨팅 연산이 굉장히 큰 경우라고 한다.

HashMap과 ConcurrentHashMap의 차이는 무엇인가? (황대선)

문제가 무엇인가?

HashMap과 ConcurrentHashMap의 차이는 무엇인가?

자신이 생각하는 답변은 무엇인가

HashMap은 key와 value에 null을 허용하고, 동기화를 보장하지 않습니다.

  • thread-safe하지 않아, 싱글 스레드 환경에서 사용하는 것을 권장합니다.
  • 스레드 간 동기화 오버헤드가 없어, 데이터를 탐색하는 속도가 빠릅니다.

concurrentHashMap은 key와 value에 null을 허용하지 않는다.

  • thread-safe해, 멀티 스레드 환경에서 사용할 수 있습니다.
  • 내부적으로 락 분할을 사용하여, 멀티 스레드에서의 성능을 향상시킵니다.

디폴트 메서드가 도입된 이유?(김수민)

문제가 무엇인가?


기존에는 인터페이스를 구현하는 클래스들이 인터페이스에 있는 메서드를 구현해야했다.
하지만, 자바 8에서 디폴트 메서드를 지원하면서 메서드를 추가적으로 구현하지 않고도 사용할 수 있도록 하였다.

디폴트 메서드의 장단점 및 함수형 인터페이스와 디폴트 메서드 간의 관계가 궁금하다.

왜 이러한 문제를 선정하였는가


  1. 함수형 프로그래밍이 도입된 자바8에서 디폴트 메서드가 어떤 기능을 제공하는지

자신이 생각하는 답변은 무엇인가


장점

디폴트 메서드를 선언하면, 해당 인터페이스를 구현한 후 디폴트 메서드를 재정의하지 않은 클래스에는 디폴트 구현이 사용된다. 이처럼 디폴트 메서드를 이용하면 기존의 코드를 건드리지 않고도 원래의 인터페이스 설계를 자유롭게 확장해줄 수 있다는 것을 확인해 볼 수 있다.

public interface InterfaceTest {
  void printJava(String java);
}
public interface InterfaceTest {
  void printJava(String java) {
    System.out.println("java");
  };
}

기존에는 추상메서드에 메서드 본문이 존재하므로 컴파일 에러가 발생했습니다.
따라서 인터페이스 내부의 추상 메서드를 재정의하기 위해 인터페이스를 상속받는 클래스에서 추상 메서드를 재정의해야했습니다.

public interface DefaultTest {
  void printJava() {
    System.out.println("java");
  };

public class DefaultClass implements DefaultTest{
}

public class Main {
  public static void main(String args[]) {
    DefaultTest obj = new DefaultClass();
    obj.printValue();
  }
}

단점

이미 구현한 객체에 오류가 발생할 소지가 있기 때문에 인터페이스에 새로운 메서드를 추가하는 것은 어려운 일이다. 모든 상황에서의 불변식을 해치지 않는 디폴트 메서드를 작성하는 것은 쉽지 않은 방법이다.

디폴트 메서드가 컴파일에 성공하더라도 기존 구현체에 런타임 오류를 일으킬 수 있기 때문에 기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 일은 꼭 필요한 경우가 아니라면 피하는 것이 좋다고 한다.

함수형 인터페이스를 도입한 자바8에서 디폴트 메서드를 추가한 이유

해당 질문에 답을 하기 위해서는 함수형 인터페이스에 대해 더 자세히 알아봐야 할 것 같습니다....

스트림 병렬화 시 스트림 소스 자료구조로 적합한 것은 무엇일까?(김수민)

문제가 무엇인가?


p254 을 보면 스트림을 구성하는 특정 자료구조가 적절한 지 확인할 수 있다.
이에 왜 이러한 자료구조가 사용하기 적절한 것인지 이유를 알아보고자 한다.
(책에서는 분해성과 병렬화 친밀도를 관련지은 것을 보니 관계성이 있을 것 같다.)

왜 이러한 문제를 선정하였는가


해당 자료구조가 젹합한 이유를 정확하게 파악하면 책에서 제시해준 자료구조 이외에도 다른 자료구조도 병렬화에 적합한 자료구조인지 판단할 수 있을 것 같기 떄문이다.

자신이 생각하는 답변은 무엇인가


ArrayList, HashMap, HashSet, ConcurrentHashMap instance는 스트림 병렬화로 성능 개선을 할 수 있는 이유?

데이터를 원하는 크기로 정확하고 손쉽게 나눌 수 있어 다수의 스레드에서 분배하기 좋은 자료구조이다.

int, long, 배열은 스트림 병렬화로 성능 개선을 할 수 있는 이유?

원소들을 순차적으로 실행할 때 참조 지역성이 뛰어나다.

참조 지역성이 뛰어나다?

이웃한 원소의 참조들이 메모리에 연속해서 저장되어 있다는 뜻
만약, 연속해 있지 않고 떨어져 있으면(즉 참조 지역성이 떨어지면) 스레드는 데이터가 주 메모리에서 주 메모리에서 캐시 메모리로 전송되어 오기를 기다리며 멍때리게 된다.

참조 지역성이 가장 뛰어난 자료구조는 기본타입의 배열이다.

주 메모리, 캐시 메모리가 참조 지역성이랑 관련이 있는 이유?

cache memory는 속도가 빠른 장치와 느린 장치 사이에서 속도차에 따른 병목 현상을 줄이기 위한 메모리이다. 대부분의 프로그램은 한 번 사용할 데이터를 다시 사용할 가능성이 높고, 그 주변의 데이터도 곧 사용할 가능성이 높은 데이터 지역성을 가지고 있다. 데이터 지역성을 활용하여 메인 메모리에 있는 데이터를 캐시 메모리에 불러와 두고, CPU가 필요한 데이터를 캐시에서 먼저 찾도록해 성능을 향상시킨다.

Stream.iterator 는 스트림 병렬화로 성능 개선을 기대할 수 없는 이유?

데이터를 원하는 크기로 정확하게 나눌 수 없기 떄문이지 않나...

중간 연산자 limit는 스트림 병렬화로 성능 개선을 기대할 수 없는 이유?

CPU 코어가 남는다면 원소 몇 개 더 처리 후 제한된 개수 이후의 결과를 버리기 때문
따라서 limit 대신 rangeClosed()를 사용하는 것이 좋다.

객체 식별성과 논리적 동치성은 무슨 차이일까?(박영규)

문제가 무엇인가?

객체의 식별성과 논리적 동치성이 무슨 차이인지 잘 모르겠다.

왜 이런 문제를 선정하였는가?

논리적 동치성이 여러 규칙을 가지고 있고 이산수학에서 가져온 것이라는 것은 알지만, 객체의 식별성도 이를 포함하고 있지 않나 생각이 들었다.

자신이 생각한 답변은 무엇인가?

수학에서 논리적 동치성이란, 합성명제 p와 q의 진리 값이 같은 경우다. p===q이며, p와 q는 같다라고 읽는다.

논리적 동치 법칙에는 항등법칙, 지배법칙 등.. 여러가지가 있다.

객체지향에서의 논리적 동치관계에는 반사성, 대칭성, 추이성 등이 있다.

객체의 식별성이란?

객체는 변할 수 있는 상태, 그리고 상태를 바꿀 수 있는 행동으로 이루어져 있기 때문에 mutable state를 가진다.

서로 다른 두 객체가 특정 시점에 완전히 동일한 상태를 가진다고 해도, 각각에 영향을 끼치는 행동의 대상이 달라질 수 있기 때문에 같은 객체라고 말할 수 없다.

같은 객체는 상태의 여부로 결정하는 것이 아닌 식별자로 구분된다.

같은 객체가 여러 시점에서 다른 상태를 지닌다고 해서 다른 객체로 분류되지 않기 때문이다.

이처럼 식별자를 기반으로 객체가 같은지 판단할 수 있는 성질을 동일성(identity)이라고 한다.

즉, 식별자 OID인 hashcode가 같은지 판단하는게 객체의 식별이라고 생각한다.

참조

스트림에 참조형을 입력했을때는 왜 부하 발생가능성이 원시형에 비해서 줄어드는가?(박영규, 김수민, 김지민)

문제가 무엇인가?

수민님의 발표에 의하면 wrapper타입을 입력했을 때, 부하가능성이 원시형에 비해서 줄어든다고 한다.
왜 그런걸까?

왜 이러한 문제를 선정하였는가

발표 도중 질문이 생겼고, 명확한 답변을 얻지 못해 이슈로 등록하기로 했다.

자신이 생각하는 답변은 무엇인가

함수형 인터페이스들에 제네릭 파라미터가 쓰여져있고, Java 에서 generic 은 참조형만 사용할 수 있다고 한다.
그렇기 때문에 자바에서는 기본형을 참조형으로 바꾸는 오토박싱이 일어난다.
기본형을 제네릭에서 사용하기 위한 참조형 타입으로 박싱하고,
다시 언박싱해서 내보내는 일련의 과정이 추가되기 때문에 병렬 스트림에서 이에 대한 오버헤드가 일어날 수 있다.

결론 : 원시형은 오토박싱과 언박싱으로 인한 부하 발생가능성이 줄어든다고 생각된다.
참조

takeWhile이란 무엇인가(김지민)

문제가 무엇인가?

takeWhile을 사용하면 무한 스트림을 포함한 모든 스트림에 프레디케이트를 적용해 스트림을 슬라이스 할 수 있다.

왜 이러한 문제를 선정하였는가

takeWhile과 filter의 구체적인 차이점이 무엇이고 왜 정확하게 설명되어 있지 않아 선정하였습니다.

자신이 생각하는 답변은 무엇인가

filter 메서드가 전체 스트림에 대한 Predicate를 판단하는 반면
takeWhile은 Predicate가 false를 반환하는 순간 나머지 요소를 전부 버린다.

따라서 무한 스트림에도 슬라이스가 가능한 이유는 takeWhile은 false가 반환되면 더이상 실행이 멈춰지는 것 즉, 스트림에서 중간 요소를 조건에 따라 끊을 수 있기 때문입니다.

image
사진 참고

  • filter 예제
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

List<Integer> result = numbers.stream()
        .filter(n -> n % 2 == 0)
        .toList();

System.out.println(result); // [2, 4, 6, 8, 10]
  • takeWhile 예제
List<Integer> numbers = Arrays.asList(1, 3, 2, 4, 6, 5, 7, 8);

List<Integer> result = numbers.stream()
        .takeWhile(n -> n < 5)
        .toList();

System.out.println(result); // [1, 3, 2, 4]

내부 상태를 갖는 연산과 내부 상태를 갖지 않는 연산(김수민)

문제가 무엇인가?


p175쪽에서
stream을 내부 상태를 갖는 연산과 내부 상태를 갖지 않는 연산으로 구분하였습니다.
따라서 해당 개념에 대해 학습하고자 합니다.

왜 이러한 문제를 선정하였는가


해당 개념을 알면 stream 이 각 요소의 연산을 어떻게 처리하는지 알 수 있을 거 같아서 선정하였습니다.

자신이 생각하는 답변은 무엇인가


상태가 없는 연산


filter, map은 데이터 요소를 처리하는 동안 이전에 처리한 데이터 요소에 대한 상태를 유지하지 않아도 됩니다.

상태가 있는 연산


reduce, sum, max 같은 연산은 결과를 누적할 내부 상태가 필요합니다.

한정된 상태가 있는 연산


스트림을 구성하는 요소의 개수와 상관없이, 관리되어야 하는 상태의 크기가 한정되는 연산으로

  • limit : 현재까지 순회된 요소의 개수 정보만 상태로 가지면 된다.
  • skip : 현재까지 스킵된 요소의 개수 정보만 상태로 가지면 된다.
  • reduce, sum, max의 경우도 현재까지 최종적인 작업 결과만을 상태로 가지면 된다.

한정되지 않은 상태가 있는 연산


sorted, distinct의 경우 스트림 내의 요소 개수에 따라 관리되어야 하는 상태의 크기가 변한다.

  • sorted : 스트림 내의 모든 데이터가 있어야 정렬 작업을 수행할 수 있다.
  • distinct : 지금까지 처리된 모든 요소가 버퍼에 추가되어있어야 한다.

어노테이션을 정의할 때 왜 예제에서 Element 명을 value로 했을까?(김수민)

문제가 무엇인가?


예시를 보면 어노테이션을 정의할 때 예제에서 Element 명을 value로 지정한 것을 확인 할 수 있다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
  Class<? extends Throwable>[] value();
}

왜 이런 문제를 선정하였는가?


element 명을 변경할 수는 없는 것일까? 라는 생각이 들었다.

자신이 생각한 답변은 무엇인가?


기본 엘리멘트는 value이며 해당 값은 @어노테이션(값)으로 바로 사용 가능하다.
즉,

    @Test(isString = "string")
    public static void m3(){
        throw new RuntimeException("실패");
    }

이런식으로 element 명이 value가 아닐 시에는 element명을 생략하지 못 하고 명시해야 한다.
하지만, element명을 value로 했을 때는 element명을 생략할 수 있다.

모듈 안으로부터 일반 패키지로부터의 모든 접근에 특별한 조취를 취해야 한다?(김수민)

문제가 무엇인가?


p.101 모듈 안으로부터 (모듈 시스템을 적용하지 않는) 일반 패키지로부터의 모든 접근에 특별한 조치를 취해야 한다.

왜 이런 문제를 선정하였는가?


  1. 모듈 간의 접근을 할 때외부에 접근할 패키지를 exports로 선언하고 해당 패키지에 접근을 하려고 하는 다른 모듈에서는 required를 선언해야 한다.
  2. 위에 저 문장은
  • 모듈 안에서 모듈 시스템이 아닌 패키지의 정보를 가져올 때
  • 모듈 시스템이 아닌 패키지에서 모듈의 정보를 가져올 때
    의 관점에서 볼 수 있을 것 같은데 어떠한 일이 발생하는지 알아보고자 이 문제를 선정했다.

자신이 생각한 답변은 무엇인가?


패키지 구조

패키지의 구조는 다음과 같다. app module, data module이 존재한다.
image

data module의 com.javs.repository 패키지 아래에 HelloRepository class가 존재한다.

public class HelloRepository {
    public String doHello(){
        return "Hello";
    }
}

app module의 com.javs.effective 패키지 아래에 HelloController class가 존재한다.

해당 클래스에서는 HelloRepository 클래스를 사용하고자 한다.
image

하지만 컴파일 에러가 발생하고
IDE의 도움을 빌려 해결방법을 찾아보면 module-info.java에 requires를 추가하라고 한다.
image

module app {
    requires data;
}
image

하지만 여전히 컴파일 에러가 발생하고
이번에는 repository package를 export하라고 한다.

module data {
    exports com.javs.repository;
}

그래서 위와 같이 해준 이후에는 에러 없이 정상적으로 수행가능하다.
(즉 모듈 간 접근을 위해서는 export, requires 가 필수적이라는 것을 깨달았다.)

그렇다면 모듈이 아닌 패키지에서 모듈 패키지에 접근하려고 할 때를 살펴보자

위의 app module, data module에 module이 아닌 패키지를 추가해주었다.
image

이때는 모듈이 아닌 시스템에서 requires, export가 없이 자유롭게 접근할 수 있다.

public class NotModule {
    public static void main(String[] args) {
        HelloRepository helloRepository = new HelloRepository();
        System.out.println(helloRepository.doHello());
    }
}

이런식으로 모듈은 모듈에서 사용할 때만 모듈의 의미가 있다.

그럼 모듈에서 모듈이 아닌 시스템의 정보는 사용가능할까?
우선 컴파일 에러가 발생한다.

그 이유는 아래에서 찾을 수 있었다.
Calling a Non Module Class from a Module Class in java 9

모듈과 모듈이 아닌 시스템이 존재할 때 모듈이 아닌 시스템에 존재하는 패키지는 무시된다. 따라서 모듈에서 모듈이 아닌 시스템을 사용하려면 모듈이 아닌 시스템의 경로를 읽을 수 있도록 ALL-UNNAMED 를 사용해야 한다.

람다를 언제 사용하는게 좋을까?(박영규)

문제가 무엇인가?

람다에 대해서 공부하면서 "람다는 코드의 양은 줄어들지만, 가독성이 나빠지는경우도 생길텐데?" 라는 생각이 들었다.
분명 람다도 사용하면 좋은 상황이 있을텐데 언제 사용하면 더 좋을까?

왜 이러한 문제를 선정하였는가

람다에 대해서 배우는 것뿐만 아니라, 어떤 상황에 적용시키면 좋을지도 알면 도움이 될 것 같다.

자신이 생각하는 답변은 무엇인가

  1. 지역적으로만 사용할 것이 확실한데 함수형 표현은 해야 하는 경우, 이를 외부에 노출하지 않을 수 있다.
  2. 반복문의 블럭 크기를 간결하게 줄일 수 있다.
  3. 반복문에 비해 프로그래머의 의도가 좀 더 분명하게 드러난다.(가독성이 좋아지는 경우도 있다.)
  4. 이터레이션을 위해서만 선언해야하는 변수를 없앨 수 있다.

그 외 더 좋은 상황이 있을지 더 고민해봤으면 좋겠다.

자바에서 비트 필드(김지민)

문제가 무엇인가?

자바에서 비트 필드를 사용할만한 경우가 무엇이 있을까요? 또 장점은 존재하지 않을까요?

왜 이런 문제를 선정하였는가?

비트 필드라는 개념이 생소한데, 이 개념의 사용하지 말라는 이유에 대해서는 납득을 하였습니다.

  • 변경 및 확장성에 취약
  • 비트 필드 값이 그대로 출력될 시, 단순한 정수 열거 상수를 출력할 때보다 해석하기 훨씬 어려움
  • 비트 필드 하나에 녹아 있는 모든 원소를 순회하기도 까다로움

그렇다면 비트필드를 사용하는 경우는 없는지 또 장점은 없는 건지 궁금하여 작성하였습니다~!

자신이 생각한 답변은 무엇인가?

장점

비트필드는 낮은 값을 갖는 정수 변수가 많을 프로그램일 때, 메모리 소비를 줄여줍니다.

사용하는 곳

자바의 접근 제한자에 비트 필드가 사용됩니다.

package java.lang.reflect;

public class Modifier {

  private Modifier() {
    throw new AssertionError();
  }

  public static final int PUBLIC = 0x00000001;
  public static final int PRIVATE = 0x00000002;
  public static final int PROTECTED = 0x00000004;
  public static final int STATIC = 0x00000008;
  public static final int FINAL = 0x00000010;
  public static final int SYNCHRONIZED = 0x00000020;
  public static final int VOLATILE = 0x00000040;
  public static final int TRANSIENT = 0x00000080;
  public static final int NATIVE = 0x00000100;
  public static final int INTERFACE = 0x00000200;
  public static final int ABSTRACT = 0x00000400;
  public static final int STRICT = 0x00000800;
}

Bit Fields
3. 비트 필드의 개념과 사용, 문제점
하지만 위 링크를 통해 문제점이 훨씬 크다는 것을 한번 더 이해할 수 있었습니다...!

외부반복과 내부반복의 차이점은 무엇일까? (박영규)

문제가 무엇인가?

for-each에서 사용하는 방법이 외부반복, 스트림은 내부반복이라고 한다.
이 두 가지 방법의 차이는 무엇일까?

왜 이러한 문제를 선정하였는가

for-each문과 스트림 방식의 차이점을 알면 외부반복과 내부반복의 차이점을 알 수 있을 것 같다.

자신이 생각하는 답변은 무엇인가

스트림은 작업이 투명하고, 다양한 방식으로 작업진행이 가능하다.
for-each문은 병렬성을 직접 관리해주어야한다.

참고

와일드 카드 in 제네릭(이도연)

와일드 카드란??

제네릭(Generic) 타입을 다룰 때 사용되는 개념이다.

  • 제한되지 않은 와일드카드 <?>
    제네릭 타입의 어떤 타입 파라미터도 받아들일 수 있다.

  • 제한된 와일드 카드 <? extends T> or <? super T>

그런데!

제한되지 않은 와일드카드에서 몇 가지 제약 사항이 있다는 점!!!

  1. 메소드 호출 제한
  2. 할당 제한

왜 열거형은 리플렉션 공격에도 안전할까?, 항상 Enum만 사용해 싱글톤을 구현해야할까?(박영규)

문제가 무엇인가?

왜 Enum은 리플렉션 공격에도 안전한지 궁금해졌다.
그리고, 항상 Enum만 사용해야할지 고민이 되었다.

왜 이런 문제를 선정하였는가?

이펙티브 자바에나온 1,2번 방법은 리플렉션에 뚫리는것을 확인했다.
3번 방법인 열거형이 안전한 생성방법이라면, 매번 Enum을 통한 싱글톤 클래스 작성을 해야하나? 고민이 들었다.

자신이 생각하는 답변은 무엇인가?

Enum으로 싱글톤이 왜 좋을까?

왜냐하면 enum은 멤버를 만들때 private로 만들고 한번만 초기화하기 때문이다.
즉, Thread-Safe하다.
또한 Enum내에서 상수, 변수, 메서드를 선언해 사용이 가능하기 때문에 독립된 싱글톤 클래스처럼 사용이 가능하다.
이 때문에 싱글톤 구현시 대부분의 경우 사용하는 것을 권장한다.

특히 직렬화가 필요하고, 인스턴스 수가 하나임을 보장받고 싶다면 Enum을 고려하면 된다.
단, 싱글톤으로 구성한 클래스가 특정 클래스의 상속이 필요한 경우는 일반 클래스로 구성해야한다.
왜냐하면 Enum은 같은 Enum 이외의 클래스 상속은 불가능하기 때문이다.

실제로 리플렉션이 막힐까?

스크린샷 2023-05-25 오후 7 55 20

실제로 테스트 코드를 작성해본 결과 리플렉션 공격이 막힌다.
java.lang.IllegalArgumentException: Cannot reflectively create enum objects 에러가 발생했으며, 이러한 이유가 궁금했다.

왜 그런지 Enum 문서를 찾아보니
Enum의 생성자는 Sole Constructor라고 한다.( 컴파일러가 사용하는 생성자라는 의미라고 판단했다.)
| It is for use by code emitted by the compiler in response to enum type declarations

즉, 컴파일러에서 사용하고 사용자가 직접호출할 수 없기 때문에 리플랙션이 불가능하다.

리플랙션은 new와 동일하게 클래스 내 생성자로 인스턴스를 사용하기 때문이다.

Enum방식의 싱글톤은 리플렉션 공격에도 안전한 이유(이진혁)

문제가 무엇인가?

싱글톤 보장 방법 세가지 중 두 가지는(public static final 필드 방식, 정적 팩토리 방식) 리플렉션에 의한 공격을 받을 수 있지만 열거형(enum방식)은 리플렉션 공격에도 완벽히 막아준다고 하였다. 그렇다면 어떻게 enum방식은 리플렉션 공격에 면역이 있을까?

왜 이런 문제를 선정하였는가?

자신이 생각하는 답변은 무엇인가?

우선 reflaction api에 대해 간략하게 설명하자면?

  • 클래스 이름만으로도 해당 클래스의 정보를 가져올 수 있는 api
    • 생성자, 필드,메서드 등등 심지어 private 접근 지정자로 선언된것도 접근이 가능하다

우리가 흔히 사용하는 spring에서 request.getParam을 써서 QueryString에 대한 정보를 얻어오고 얻어온정보(String타입) 를 우리가 원하는 타입으로 변환 시켜주는 작업 대신에 main메소드의 parameter에 우리가 원하는 타입과 받을 정보(변수)를 지정해주면 알아서 정보를 받아와 타입변환까지 해주는데 …

바로 여기에서 reflaction api를 이용하여 자동으로 해주는것

그렇다면 이런 작업이 어떻게 가능할까?

자바에서는 컴파일시 컴파일러가 우리가 작성한 소스코드를 바이트 코드로 변환시켜준다. 그리고 런타임시 변환시킨 바이트 코드는 클래스 로더를 통해 JVM 메모리영역(static)에 저장이됩니다. reflection은 이 static영역을 다 뒤지면서 클래스 이름과 관련된 정보를 전부 조회한다.

열거방식(enum)은 어떻게 리플렉션 공격에 면역이 있을까?

그 이유는 Enum 클래스가 설계되어 있는 방식 때문이다. Enum 클래스는 내부적으로 private 생성자를 가지며 이 생성자는 컴파일러에 의해 자동으로 만들어집니다.(개발자가 호출할 수 없고 컴파일러에 의해 관리됨)

Enum은 기본적으로 프로그램이 컴파일될 때 모든 값이 결정되는 정적인 특성을 가지고 있다. 이들 값은 상수이며 프로그램의 실행 도중에 변경되지 않는다.

이러한 Enum의 성질과 설계원칙을 지키기 위해서 java.lang.IllegalArgumentException 예외를 통해 의도적으로 막은것 같다.

아이템37의 두번째 코드가 불편해요(박영규)

문제가 무엇인가?

item37의 두번째 코드 가독성이 너무 불편해요

private static final Map<Phase, Map<Phase, Phase.Transition>>
                m = Stream.of(values()).collect(Collectors.groupingBy(t -> t.from,
                () -> new EnumMap<>(Phase.class),
                Collectors.toMap(t -> t.to, t -> t,
                        (x, y) -> y, () -> new EnumMap<>(Phase.class))));

왜 이런 문제를 선정하였는가?

어떻게하면 리팩토링 할 수 있을까요?

자신이 생각한 답변은 무엇인가?

Map<Phase, Phase.Transition>을 클래스로 빼면 가독성이 조금 더 좋아질까 싶은데..

사실 어떻게 리팩토링할지 잘 모르겠네요 여러분 생각은 어떤가요

쇼트셔킷이란 무엇일까?(김수민)

문제가 무엇인가?


allMatch, noneMatch, findFirst, findAny 연산은 모든 스트림 요소를 처리하지 않고 결과를 반환한다고 합니다.
그럼 쇼트셔킷이란 무엇이길래 스트림의 모든 요소를 처리하지 않고도 결과를 반환할 수 있는 것일까요?

왜 이러한 문제를 선정하였는가


p168쪽에서 쇼트셔킷이 소개되었지만, 어떠한 원리인지 소개가 되어있지 않아
이에 대해 알아보고자 선정하였습니다.

자신이 생각하는 답변은 무엇인가


쇼트셔킷이란 불필요한 연산을 생략함으로써 성능을 개선하는 연상 방식입니다.

List<Menu> strings = List.of("pork","beef","chicken","piza","icecream");
List<String> names = menu.stream()
    .filter(d -> {
        System.out.println("filtering" + d.getName());
        return d.getCalories() > 300;
    })
    .map(d -> {
            System.out.println("mapping" : g.getName());
            return d.getName();
    })
    .limit(3)
    .collect(toList());

// filtering pork
// mapping pork
// filtering beef
// mapping beef
// filtering chicken
// mapping chicken

3개를 찾았기 때문에 더 이상 연산을 수행하지 않는 것을 알 수 있습니다.

왜 싱글턴 인스턴스는 mock으로 대체할 수 없을까?(김수민)

문제가 무엇인가?

p23 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기가 어려울 수 있다. 타입을 인터페이스로 정의한 다음 그 인터페이스를 구현해서 만든 싱글턴이 아니라면 싱글턴 인스턴스를 가짜(mock)구현으로 대체할 수 없기 때문이다.

이 부분에서 싱글톤 인스턴스는 mock객체를 생성할 수 없음으로 이해하였다.
왜 싱글턴 인스턴스는 mock객체를 생성할 수 없는 것일까?

왜 이러한 문제를 선정하였는가

자신이 생각하는 답변은 무엇인가

interface를 Mock하면 interface를 임시적으로 구현한 껍데기 클래스를 만든다. 이 객체가 바로 가짜 객체이다.
가짜 객체를 만들 때는 리플렉션을 이용한다.(그러나 private 생성자는 리플렉션으로 가져오지 않는다.)

따라서 싱글톤 클래스의 생성자는 private이기 때문에 가짜 객체를 만들 수 없다.
또한 정적 메서드를 사용할 수가 없다.(모의 객체는 인스턴스인데, 정적 메서드같은 경우는 클래스� 수준에서 정의되는 메서드이기 때문이다.)

따라서 Mockito는 정적 메서드를 mock할 수 없기 때문에 가짜(mock)를 주입하기 어렵다. 대신 static 메서드를 mocking할 수 있는 PowerMock같은 도구를 사용하면 가능해진다.

어노테이션을 생성할 때 왜 @interface라는 예약어로 했을까? @class는 고려를 안해봤을까??(이도연)

문제가 무엇인가?

  • 어노테이션을 생성할 때 왜 @interface라는 예약어로 했을까? @Class는 고려를 안해봤을까??

왜 이런 문제를 선정하였는가?

  • 예약어를 선정할 때 과연 제임스 아서 고슬링씨는 어떤 고민을 하면서 예약어를 만들까? 각각의 예약어마다 고민을 하면서 만들까?

자신이 생각한 답변은 무엇인가?

  • @interface는 어노테이션을 생성하는 예약어이다. 이 예약어를 지정할 때 고슬링씨는 @Class, @interface 다른것을 고려하지 않았을까?
    이야기를 하기전에 클래스와 인터페이스를 한번 짚고 넘어가자
    클래스는 구제척인 동장 방식이라고 볼 수 있다. 인터페이스는 동일한 목적 하에 동일한 기능을 보장하게 해준다. 또한 추상화를 시킬 수 있다.
    자 돌아와서 어노테이션을 다시 생각해보자. 어노테이션은 구체적인 동작 방식이라고 볼 수 있나? 그것보다는 동일한 기능을 제공한다. 추가로 추상화 또한 가능하다.
    그래서 고슬링씨가 어노테이션 생성 예약어를 @interface로 고민하지 않았을까 생각한다.

전략 패턴이란 무엇인가(김지민)

문제가 무엇인가?

동작 파라미터화에서 사과를 선택하는 전략에 대해 전략 패턴을 적용한다.

왜 이러한 문제를 선정하였는가

그렇다면 전략 패턴이란 무엇인지에 대해 정확히 이해하고 넘어가면 좋을 것 같아 선정하게 되었다.

자신이 생각하는 답변은 무엇인가

전략패턴이란

  • 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴
  • 전략이란
    • 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘 등
  • 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고,
  • 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴이다.

인터페이스에서 static 메서드와 default메서드(박영규)

문제가 무엇인가?

인터페이스에서 static메서드와 default메서드의 역할이 확장한다는 흐름에서 비슷한 것 같은데 왜 둘 다 도입한 걸까요??

왜 이러한 문제를 선정하였는가

static & default 메서드를 interface를 확장함에 있어 어떻게 사용해야하는지 명확하게 짚고 넘어가고 싶었습니다.

자신이 생각하는 답변은 무엇인가

static & default 메서드의 차이점은 재정의가 가능한지, 참조변수를 통해 호출 가능한지 여부 같습니다.
interface를 확장할 때, 재정의가 가능해야하는 메서드라면 defualt메서드로 작성하는게 좋을 것 같습니다.
혹은 재정의할 필요가 없고, Util적인 측면이 강하다면 static 메서드로 작성하는게 좋을 것 같습니다.
요약하자면
interface의 default 메서드

  • interface에서도 메서드 구현이 가능하다.
  • 참조 변수로 함수를 호출할 수 있다.
  • implements한 클래스에서 재정의가 가능하다.

interface의 static 메서드

  • interface에서 메서드 구현이 가능하다.
  • 반드시 클래스 명으로 메서드를 호출해야 한다.
  • 재정의 불가능

참고

HashMap에서 key와 value에 null값을 허용하는 이유가 무엇일까? (황대선)

문제가 무엇인가?

HashMap에서 key와 value에 null값을 허용하는 이유

왜 이런 문제를 선정하였는가?

Map 인터페이스의 put 메서드는 key값과 value값에 null을 허용하지 않고 있다.

스크린샷 2023-06-01 오후 4 45 15

출처

하지만, HashMap은 key와 value에 Null을 허용합니다. 왜 그런 것일까요??

자신이 생각한 답변은 무엇인가?

먼저, HashMap을 살펴보겠습니다.

HashMap의 put 메서드와 hash 메서드는 아래와 같습니다.

public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }

static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }

위 코드를 보면, key값을 받아 hash 메서드를 사용해 만든 값을 고유키로 사용하는 것을 알 수 있고, hash 메서드에서 key 값이 null이면 고유키로 0을 사용하는 것을 확인할 수 있습니다.

그렇다면, 다시 질문으로 돌아와서 왜 key에 null을 허용한 것일까요?

만약 HashMap의 key값에 null에 관한 처리를 하지 않았더라면, 이를 사용하는 라이브러리나 개발자들이 null에 관한 처리를 해주어야합니다.

이는 개발자들이 이를 사용할 때 null 처리 리소스를 없앨 수 있습니다. 또한, key의 null 처리에 관하여 HashMap에서 정의해주었기 때문에 HashMap을 사용하는 개발자나 라이브러리들이 이를 통일성있게 이용할 수 있습니다.

출처

예외의 클래스 파일이 런타임시에 사라지는 경우??(김지민)

문제가 무엇인가?

P.242 해당 예외의 클래스 파일이 컴파일 타임에는 존재했으나 런타임에는 존재하지 않을수도 있다. 라고 나옵니다.

왜 이런 문제를 선정하였는가?

  • 컴파일 타임에는 존재했으나 런타임에는 사라지는 상황이 어떤 예시가 모르겠어요...

자신이 생각한 답변은 무엇인가?

찾아보니 예외의 클래스 파일이 사라지는 경우가 아니라, 자바 전체의 클래스 파일에 해당되는 내용이었습니다.
자바 클래스 로딩에 대한 지식이 조금 부족했던 것 같습니다.

클래스 로더

Java는 동적 로딩(Dynamic Loading)을 하는 특징을 지님

  • 이 동적 로딩을 담당하는 부분이 JVM에서의 클래스 로더

즉, 클래스 로더가 Java로 작성된 프로그램의 런타임(Runtime) 중에, JVM의 메소드 영역에 동적으로 Java 클래스를 Load 하는 역할을 하는 것이다.

  • 예시
interface AnimalIntegerface {
	void sound();
}
//
public listenAnimalSound(AnimalInterface i) {
	i.sound(); 
}

자바의 인터페이스 다형성은 프로그래머로 하여금 선언부와 구현부를 나누어 프로그래밍 할 수 있게 해줌

  • 위에서 listenAnimalSound()에서 변수로 입력되는 인터페이스 (실제로는 AnimalInterface를 구현한 클래스만이 변수로 입력될 수 있습니다)는, 메소드가 실행되기 전까지는 어떤 클래스가 들어올지 알 수 없습니다
  • 따라서 이는 메서드가 실행되는 순간인 런타임에 동적으로 로딩되게 됩니다!
    참고

네이밍을 잘하는 방법(김지민)

문제가 무엇인가?

9장의 리팩토링 얘기에서 람다 표현식을 메서드 참조로 리팩터링하는 것에 대한 내용이 나왔습니다.
메서드 참조의 메서드명으로 코드의 의도를 명확하게 알릴 수 있기 때문인데요.
그렇다면 좋은 메서드 네이밍에 대해서 생각해볼까요?

왜 이러한 문제를 선정하였는가

공부를 하다가 이해가 안되는 부분을 공부할 때 예제를 찾아보면서 이해하려고 합니다.
남의 코드를 보다보면 네이밍의 중요성을 느끼고 저도 좋은 네이밍을 짓기 위해 노력하게 됩니다.

자신이 생각하는 답변은 무엇인가

책 클린코드에서는

  1. 의도를 분명히 밝혀라
  • 이름에는 존재 의도, 기능, 사용법이 잘 드러나게 지어야 한다.
  1. 그릇된 정보를 피하라
  • 중의적으로 해석될 수 있는 이름은 짓지 않아야 함
  • 흡사한 이름을 사용하지 않도록 조심하라
  1. 의미있게 구분하라
  • 의미없는 구분 X
  • 숫자 하나로 구분 X
  1. 발음하기 쉬운 이름을 사용하라
  2. 검색하기 쉬운 이름을 사용하라
  • 상수를 여러 곳에서 사용한다면 이름을 붙여 사용
  • 변수의 이름의 길이는 변수의 범위에 비례해서 길어짐
  1. 인코딩(변수에 부가 정보를 덧붙여 표기하는 것)을 피하라
  • 멤버 면수에 접두어를 붙이지 않음
  1. 메서드 네이밍시 동사 혹은 동사구를 사용하라
  • 동사 혹은 동사구를 사용하라
  • 접근자, 변경자, 조건자는 get, set, is로 시작
  1. 기발한 이름을 피하라
  2. 한 개념의 한 단어를 사용하라
  3. 의미있는 맥락을 추가하라
  4. 불필요한 맥락을 없애라

등이 있습니다. 다들 한번씩 보시면 좋을 것 가타요^_^]

https://his2070.tistory.com/6
https://tecoble.techcourse.co.kr/post/2020-04-26-Method-Naming/

Cloneable을 구현한것 만으로는 외부객체에서 clone 메서드를 호출 할 수없는이유(+리플렉션을 이용하면 호출할 수있는것아닌가?) (이진혁)

1. 문제가 무엇인가?

Cloneable을 구현한 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다.

리플렉션을 이용하면 가능하나 100% 성공하는것은 아니다.

  • 해당 객체가 접근이 허용된 clone 메서드를 제공한다는 보장이 없기 떄문이다.

2. 왜 이러한 문제를 선정하였는가

내가 이해한 리플렉션은 만능이다. 그런데 100%성공 하는것은 아니고 그 이유가 해당 객체가 접근이 허용된 clone 메서드를 재공한다는 보장이 없기 때문이라는 이유가 납득이 되지않았다.

3. 자신이 생각하는 답변은 무엇인가

Cloneable을 구현한 것만으로는 외부 객체에서 clone 메서드를 호출 할 수 없다.

Clone()메소드는 Object 클래스에 protected로 정의되어있다. 모든 클래스는 직접적이든 간접적이든 Object클래스를 상속받고있음으로 모든클래스에서 clone()메소드에 접근할 수 있다. 하지만 다른 클래스의 인스턴스로 clone()메서드를 호출할 수는 없습니다.(public이 아니기 때문에)

그렇다면 리플렉션을 사용하면 clone메소드를 호출가능은한데 100%성공하는것은 아니라는이유가

“해당 객체가 접근이 허용된 clone 메소드를 제공한다는 보장이 없기 때문이다.” 라는 것은 도대체 무슨 의미일까요?

책에서는 clone메소드를 public으로 재정의 해서 객체에서 호출가능하게끔 만들라는데 리플렉션에 setAccessible(true)를 이용하면 protected로 되어있던 것을 public처럼 호출이 가능하다….(이 말은 100%된다는거 아닌가)

그래서 다른 시각으로 “해당 객체가 Object로부터 파생되지않다는 것인가?”라는 생각을 하게되었다. 그 이유는 리플렉션은 해당 클래스 뿐만아니라 상속받는 클래스, 인터페이스까지 조회가 가능한데 해당 객체가 Object랑 관련이 없으면 Object에 정의된 clone() 메소드에 정보를 가져올 수 없기 때문에 그런생각을 하게되었다.

하지만 모든 클래스는 “Object클래스를 모든 클래스의 최상위 클래스 아니던가?? 객체가 Object클래스와 관련이 없을 수 있는건가?” 라는 생각이 들었고 해당 객체가 접근이 허용된 clone 메서드를 제공한다는 보장이 없기 떄문이다. 라는 문구에 납득하지 못했다.

따라서 내린 결론은 Object 클래스와 관련없는 객체가 등장하지않는 이상

Cloneable 인터페이스만 구현해도 외부 객체에서 (리플렉션을 통해) 100%접근 가능하다라고 결론을 지었다.

groupingBy와 partitioningBy(김수민)

문제가 무엇인가?


  • 그룹화 groupingBy : 스트림 요소를 Function 매개변수로 받는다.
  • 분할 partitioningBy : 스트림 요소를 Predicate 매개변수로 받는다.

만약, groupingBy 함수도 boolean을 return 한다면
이 상황에서는 groupingBy를 쓰나, partioningBy를 쓰나 결과는 같을 것 같다.
왜냐면 groupingBy는 그룹화 함수가 반환하는 키 그리고 각 키에 대응하는 스트림의 모든 항목 리스트를 값으로 갖는 맵이 결과이고
partioningBy에서도 맵의 키는 boolean이기 때문이다.

그럼 무엇을 쓰는 게 더 좋을까?

왜 이러한 문제를 선정하였는가


결과가 동일시하게 나오는 경우엔 무엇을 쓰는 게 성능 측면에서 더 좋을지 궁금해졌다.
지금 생각으로는 partioningBy는 predicate를 인수로 받기 때문에 해당 함수를 쓰는 게 의미상으로는 더 맞지 않을까 생각한다.
만약, 내 생각이 옮다면 그 이유는 무엇이고, 옳지 않다면 왜 groupingBy의 성능이 더 좋은지 알고 싶다.

자신이 생각하는 답변은 무엇인가


스트림을 두개의 그룹으로 나눠야하는 경우 partitioningBy()가 더 빠르다.
아직 왜 그런지는 모르겠다........

여전히 모르겠습니다... 다같이 이야기 나눠보아요!

Entity에서 Wrapper클래스를 사용하는 게 맞는 것인가 (황대선)

문제가 무엇인가?

Entity에서 Wrapper클래스를 사용하는 것이 맞는 것인가

왜 이러한 문제를 선정하였는가

34p에 “박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의하자”

라는 문장에서 “제 기존 코드에서 박싱된 기본 타입을 가장 많이 사용하는 데는 어디이고, 그 곳에서는 박싱된 기본 타입을 사용하는 것이 옳은 것인가?” 라는 의문이 들었습니다.

자신이 생각하는 답변은 무엇인가

박싱된 기본 타입은 아래와 같은 특성을 가집니다.

  1. 값을 래핑함으로써 객체 지향적인 기능을 사용할 수 있게 한다.
  2. 기본 타입과 비교할 때, 식별이 가능하다.
  3. null 값을 가질 수 있다.
  4. 값의 동등성을 비교하는 등 추가적인 기능을 제공한다.
  5. 박싱된 기본 타입은 불변성을 가지기 때문에 값을 공유하는 동안 변경될 걱정이 없어 예측이 가능합니다.
  6. 기본 타입과 비교했을 때, 메모리 사용량이 더 많고, 연산 속도가 느립니다.

2,4번 특성에의해, 엔티티에서 기본 타입을 필드로 가지는 것은 유용할 수 있을 것 같습니다.

하지만, 값의 변경이 자주 일어나는 필드의 경우에는 기본 타입으로 하는 것도 괜찮은 것 같습니다.

결론, PK에는 Long(Wrapper 클래스)를 사용하고, Null을 사용할 필요가 없고, 변경이 자주 일어나는 필드의 경우에는 기본 타입으로 필드를 사용하는 것도 괜찮을 것 같습니다.

스크린샷 2023-05-24 오후 11 33 04

사진 출처

BE개발에서 문서화는 어디까지 해야할까요?(박영규)

문제가 무엇인가?

프로젝트를 하면서 문서화를 어떤것까지 해야할까요?

왜 이런 문제를 선정하였는가?

item19에서 상속을 설명하며 문서화의 중요성을 이야기 하였습니다.
여태까지 프로젝트를 진행하면 API문서 요구사항 등 이러한 유지보수보다는 개발을 위한 문서화만 했는데,
앞으로 유지보수까지 생각하며 해야할 문서화는 어떤 것이 있을까요?

자신이 생각한 답변은 무엇인가?

저는 여태까지 API, SRS, 여러 Diagram 등 개발을 위한 문서화만 했는데, 유지보수를 위한 문서화에는 주석을 통한 문서화도 있더라구요.

찾아보니 시스템 아키텍처, 코드 사용법, API 문서, 개발 및 배포 프로세스 등이 있다고 합니다.

제가 프로젝트에 적용할만한 문서화는 시스템 아키텍처, 코드 사용법(JavaDocs), API문서, 개발 및 배포 프로세스 등이 있을 것 같네요.

더 추가할만한 문서화가 있을까요?

Serializable을 구현하면 왜 공개API가 될까?(박영규)

문제가 무엇인가?

Serializable을 구현한 클래스에서는 그 필드들도 의도치 않게 공개 API가 될 수 있다.

private & package-private 멤버는 해당 클래스 구현에 해당하므로, 보통 공개 API에 영향을 안준다.
근데 얘는 왜 공개API가 될 수 있을까?
그리고 사용하는 것을 비추천하는데 자바 내부구현에는 왜 많이 사용되었을까?

왜 이런 문제를 선정하였는가?

너무 뜬금없이 반례가 나오기도 했고, 앞서 중요하다고 했던 정보은닉과 캡슐화가 깨지기 때문에 잠깐 언급한듯 하여 왜 캡슐화가 깨지게 되는지 궁금했다.
또한 사용하는데 위험성이 많은데, 자바 내부구현에 많이 사용된 이유가 궁금했다.

자신이 생각한 답변은 무엇인가?

직렬화가 무엇일까?
직렬화: 데이터를 Stream 으로 전송할 수 있는 상태로 만든다 (ex. 파일)
역직렬화: Stream 으로 전송 받은 데이터를 객체로 만든다
근데 공개 API가 되는 이유는 잘 모르겠다.

# hashmap구현 (java 11)
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;

자바 11에서 HashMap을 구현한 것을 보면 Serializable 을 구현하면 byte stream encoding 도 하나의 공개 API 가 된다.
private, package-private 인스턴스 필드들도 API 로 공개하는 꼴이 된다. 캡슐화가 깨진다.
JSON같은 대체방안을 사용하면 parser를 사용해야하니까, 자바 내부에서는 그냥 직렬화를 쓰는게 아닐까??

델리게이션이란(김지민)

문제가 무엇인가?

옳지 못한 상속(p.424)에서 나오는 델리게이션이란 무엇인가요.

왜 이러한 문제를 선정하였는가

13장에는 다중 상속을 이용해서 기존 코드를 재사용하는 내용이 나왔다.
또 그 반례에 대해 옳지 못한 상속 이야기가 짧게 나와있는데, 이 해결방법으로 나온 델리게이션에 대해 더 알아보고자 선정하게 되었습니다.

자신이 생각하는 답변은 무엇인가

위임(Delegation)이란

  • 다른 클래스를 멤버로 포함하는 형태로 정의
  • 즉, has a 관계로 클래스 내에서 위임 관계에 있는 클래스의 인스턴스를 가지고 있는 상태
  • 말 그 자체로 어떤 행위를 위임 관계에 있는 객체에게 넘겨서 처리하는 것

Printer 클래스가 있고, 이 클래스는 문자열을 출력하는 print() 메서드가 있다고 가정

public class Printer {
    public void print(String message) {
        System.out.println(message);
    }
}

Printer 클래스의 print() 메서드를 사용하면서 새로운 클래스 Report를 작성하려면, 상속 대신 델리게이션을 사용 가능
즉, Report 클래스는 Printer 객체를 멤버 변수로 가지고, Printer 객체의 print() 메서드를 호출하는 메서드를 작성

public class Report {
    private Printer printer;

    public Report(Printer printer) {
        this.printer = printer;
    }

    public void generateReport() {
        // Report Generation Logic
        String report = "This is a report.";
        printer.print(report);
    }
}
  • Report 클래스는 Printer 클래스를 상속받지 않고, Printer 객체를 멤버 변수로 가지고 있음
  • generateReport() 메서드에서는 Printer 객체의 print() 메서드를 호출하여 보고서를 출력

Printer 클래스의 모든 기능을 Report 클래스에서 사용할 수 있지만, Report 클래스는 Printer 클래스의 모든 메서드와 필드를 상속 받지 않으므로 코드 재사용 문제를 피할 수 있음!

시그니처란 무엇인가(김지민)

문제가 무엇인가?

함수 디스크럽터란, 함수형 인터페이스의 추상 메소드의 시그니처를 서술하는 메서드라고 서술되어 있습니다.

왜 이러한 문제를 선정하였는가

책에 나온 퀴즈의 예제들을 보았는데 시그니처라는 것에 대해 직관적으로 이해가 잘 되고 부가 설명이 부족한 것 같아 선정하였습니다.

자신이 생각하는 답변은 무엇인가

람다 표현식의 시그니처를 서술하는 메서드를 함수 디스크립터라고 부른다.

함수형 인터페이스의 추상 메서드를 람다로 구현하였다면, 둘의 표현식 시그니처는 같아야 한다.
예를 들어 함수형 인터페이스 Comparator의 compare 메서드의 함수 디스크립터는 (T, T) → int이다.

위 설명과 책의 아래의 예제들(p.96)을 보고 제가 생각한 시그니처의 의미는 메서드의 리턴 타입과 파라미터 리스트였습니다.
Screen Shot 2023-02-23 at 9 06 27 PM
Screen Shot 2023-02-23 at 9 06 32 PM

하지만 제가 추가로 찾아본 시그니처란

  • 컴파일러는 메소드의 시그니처를 이용해서 메소드를 구분한다.
  • 자바에서의 메서드 시그니처 의미는 메서드 명과 파라미터의 리스트를 의미한다
  • 여기서 파라미터 리스트라는 것은 파라미터의 타입, 개수, 순서를 말한다
  • 메소드의 리턴 타입은 포함되지 않는다!!

였습니다. 마지막 리턴 타입은 포함되지 않는다고 작성되어 있는데요. 어떤 부분이 문제인 걸까요...? 제가 예제를 잘못이해한 것일까요?

게으르게 계산함으로써 얻는 이점이 뭘까?(김수민)

문제가 무엇인가?

책에서는 스트림은 게으르게 만들어지는 컬렉션과 같다 라고 하였다. 게으르다? 이 말이 어떤 관점에서 게으르다는 말일까? 게으르다고 하면 성능에 부정적인 영향을 끼칠 것 같은데 스트림을 이용하는 이유가 무엇일까?

왜 이러한 문제를 선정하였는가

스트림과 컬렉션의 차이점이 게으른 연산인 것 같은데, 이 게으른 연산 파악을 통해 스트림을 잘 사용할 수 있을 것 같아서이다.

자신이 생각하는 답변은 무엇인가

스트림과 컬렉션은 둘다 연속된 요소 형식의 값을 저장하는 자료구조 인터페이스를 제공한다.

둘의 차이점은 무엇일까?

  1. 데이터를 언제 계산하는가?
  • 컬렉션 : 모든 요소는 컬렉션에 추가되기 전에 계산된다.
  • 스트림 : 요청할 때만 요소를 계산한다. 따라서 불필요한 계산을 하지 않아도 된다.
  1. 메모리 저장
  • 컬렉션 : 모든 데이터를 메모리에 저장하고 로직을 처리한다
  • 스트림 : 각 데이터를 메모리에 저장할 필요 없이 바로 계산할 수 있다.

메서드 참조의 인자(김지민)

문제가 무엇인가?

왜 메서드 참조는 인자를 받지 않게 했을까요

왜 이런 문제를 선정하였는가?

List<String> letters = Arrays.asList("a","b","c");

System.out.println("Lambda upperCase forEach");
letters.forEach(l -> System.out.println(l.toUpperCase())); // 통과

System.out.println("Method Reference upperCase forEach");
letters.forEach(System.out::println(String::toUpperCase)));  // 컴파일 에러

위와 같은 상황에서 메서드 참조의 경우에는 인자를 받을 수 없기 때문에 에러가 발생합니다.
왜 메서드 참조는 파라미터를 받지 못하게 했을지 궁금해져서 가져왔습니다.

자신이 생각한 답변은 무엇인가?

제가 생각하기로는

  • 메서드 참조의 장점인 간결한 표현을 살리기 위해서 일수도 있을 것 같고
  • 메서드 참조는 메서드를 호출하는 것이 아닌 메서드 자체를 참조하는 것인데 이것과도 관련된 문제일 수도 있을 것 같습니다..

일급 시민이란 무엇인가(김지민)

문제가 무엇인가?

자바 8에서의 메서드는 더 이상 이급값이 아닌 일급값이라고 서술되어 있다.
그렇다면 일급 값의 의미는 무엇이고 일급 시민은 무엇인가?

왜 이러한 문제를 선정하였는가

일급이라는 개념에 대해 이해하고, 자바 8의 메서드 참조라는 새 기능과 람다에 대해 다시 한번 생각해볼 수 있을 것 같아서 선정하였다.

자신이 생각하는 답변은 무엇인가

일급 함수 : 함수가 다른 일급 객체와 동일하게 다루어 질 때, 일급 함수라고 지칭함

  • 익명함수들은 공통으로 일급 객체라는 특징을 가지고 있다

일급 객체(일급 시민)란?

아래의 3가지 조건을 만족하는 객체를 일급 객체라 부름

  1. 변수에 할당 할 수 있어야 한다.
  2. 객체의 인자로 넘길 수 있어야 한다.
  3. 객체의 리턴값으로 리턴 할수 있어야 한다.

팩토리 메서드란(김지민)

문제가 무엇인가?

6절에서 팩토리 메서드에 대한 이야기가 나온다. 팩토리 메서드란 무엇일까?

왜 이러한 문제를 선정하였는가

Collector에 대한 이해를 하기 위해서는 팩토리 메서드에 대한 이해가 필요하다고 생각하였다.

자신이 생각하는 답변은 무엇인가

팩토리 메서드란

  • 객체를 생성 반환하는 메서드이며 디자인 패턴 중 하나
  • 객체를 생성하는 과정을 추상화하여, 객체 생성 과정을 클라이언트에서 분리하는 것을 목적으로 함
    • 일반적으로 객체를 생성하는 코드는 객체의 클래스명과 생성자를 사용하여 직접 작성
    • 하지만 팩토리 메서드 패턴을 사용하면 객체를 생성하는 코드를 팩토리 메서드로 분리

BigInteger answer = BigInteger.valueOf(42L); // BigInteger 42를 리턴한다
는 사실 new BigInteger(...)를 은닉하고 있다

실체화란 무엇인가?(김수민)

문제가 무엇인가?

E, List<E>, List<String>과 같은 타입을 실체화 불가 타입이라 한다. 쉽게 말해, 실체화되지 않아서 런타임에는 컴파일타임보다 타입 정보를 적게 가지는 타입이다. 소거 메커니즘 때문에 매개변수화 타입 가운데 실체화될 수 있는 타입은 List<?>와 Map<?,?>과 같은 비한정적 와일드카드뿐이다.

왜 이런 문제를 선정하였는가?

  1. 런타임에 컴파일타임보다 타입 정보를 적게 가진다는 말이 무엇일까?
  2. 와일드 카드는 왜 실체화될 수 있는 타입이라고 하는 것일까?

자신이 생각한 답변은 무엇인가?

1. 런타임에 컴파일타임보다 타입 정보를 적게 가진다는 말은 무엇일까?


ORACLE Java Dcoumentation : Non-Reifiable Types

실체화 불가 타입(Non-Reifiable Type) : 런타임에 타입 정보를 갖고 있지 않고, 컴파일타임에 타입 소거가 되는 타입이다.

이와 반대로

실체화(refiable)이 있다.
이는 타입 정보를 런타임에 완벽하게 사용할 수 있는 타입이다.

2. 와일드 카드는 왜 실체화될 수 있는 타입이라고 하는 것일까?


Reifiable vs Non-Reifiable

비한정적 와일드카드는 타입 정보를 애초에 명시하지 않았기 때문에
컴파일시에 타입 소거를 한다고 해도 잃을 정보가 없다.
따라서 실체화 타입이라고 한다.
(추가적으로 실체화 타입은 타입 소거시에 컴파일 시점에 Object로 변환된다.)

inner class가 아닌 static inner class를 사용하라고 하는 이유(김수민)

문제가 무엇인가?


p.97 한 클래스에서만 사용하는 package-private 톱 레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static으로 중첩시켜보자.
따라서 위의 97페이지에서 말하는 것도 pirvate static으로 줄이면 해당 클래스의 바깥 클래스에서만 접근 가능하므로 접근 수준을 줄이는 것에 의의가 있을 것 같습니다.

왜 이런 문제를 선정하였는가?


inner class가 아닌 static inner class로 만들라고 한 이유가 궁금합니다.

자신이 생각한 답변은 무엇인가?


public class OuterClassEx {
    private int OuterClassVariable;
    
    public class InnerClass{
        private int InnerClassVariable;
        
        private void doPrint(){
            System.out.println(OuterClassVariable);
        }
    }
}

inner class에서 outer class 의 변수에 접근 가능하다.
이유를 알아보면 컴파일을 했을 때 아래와 같이 inner class의 생성자에서 Outerclass의 생성자를 가지고 있음을 확인할 수 있다.

public class OuterClassEx$InnerClass {
    private int InnerClassVariable;

    public OuterClassEx$InnerClass(OuterClassEx var1) {
        this.this$0 = var1;
    }

    private void doPrint() {
        System.out.println(this.this$0.OuterClassVariable);
    }
}

반면, static inner class로 선언했을 때는 inner class에서 outer class의 변수를 사용하지 못한다.(컴파일 에러 발생)
image

하지만 static inner class로 선언했을 때는 inner class에서 외부 클래스에 접근하지 못 한다.

public class OuterClassEx$InnerStaticClass {
    private int InnerClassVariable;

    public OuterClassEx$InnerStaticClass() {
    }
}

이런 관점에서 보면 static inner class보다 inner class를 사용하는 게 더 좋지 않나? 라는 생각이 든다.
하지만 본래는 서로 다른 클래스였고, OuterClassEx에서만 InnerClass를 사용하고 있어서 내부 클래스로 가져온 것이다.
이러한 관점에서 봤을 때는 inner class에서 외부 클래스의 변수를 사용하지 못 하는 게 더 올바를 것 같다.

하지만 더 자세한 이유는 item 24에서 확인해보자!
이 외에도 inner class는 outer class를 사용하지 않더라도 내부 참조 때문에 GC의 대상이 안 된다.
하지만, static inner class는 outer class에 대한 참조가 없었기 때문에 내부 클래스를 생성할 때만 사용하고 이외에는 외부 클래스는 GC의 대상이 된다.

아래 참고자료에서는 VisualVM을 통해서 static inner class 와 inner class를 사용할 때 메모리 할당 상태를 시각적으로 볼 수 있는 방법을 제공하였다!!!! 꼭 아래 자료도 같이 보길 추천!!!
참고

++추가
그럼 static inner class를 사용하자! 로 결론낼 수 있을 것 같은데
실제로 자바 내부 API도 inner class를 사용한 경우가 존재했다.
inner class도 사용하는 경우가 따로 있을 것 같은데 이는 item24에서 나오니 여기서 같이 알아보자!

익명 클래스와 람다?(김수민)

문제가 무엇인가?


책에서는 익명 클래스를 사용할 경우 동작을 정의하는 메서드를 구현해야 하기에 람다 표현식으로 바꾸었습니다.

왜 이러한 문제를 선정하였는가


그래서 저는 그럼 익명 클래스로 사용하는 경우는 모두 람다 표현식을 사용하는 것으로 변경해야 하나?라는 생각이 들었고,
위의 생각이 맞는 것인지 아니면 책과 같은 상황은 람다 표현식을 사용하는 것이 좋고
반대로 익명 클래스를 사용하는 것이 좋은 상황이 있는 것인지 궁금합니다.

자신이 생각하는 답변은 무엇인가


책에서 발전한 과정처럼
익명 클래스를 인스턴스를 함수의 파라미터로 넘기는 것은 낡은 기법이라고 헌더!!!
왜냐하면 람다를 이용하면 간결하게 표현할 수 있기 때문이다.

하지만 람다는 이름이 없기 때문에 문서화가 어려워 코드 자체로 표현이 안 될때는 람다를 쓰지 않는 것이 좋다고 한다.

또한 람다는 다음과 같은 상황일 때 람다가 대체할 수 없다고 한다.

  1. 추상클래스의 인스턴스를 만들 때
  2. 추상메서드가 여러 개인 인터페이스의 인스턴스도 람다로 표현 불가능
  3. 람다의 this는 바깥 인스턴스를 가리킨다.

우선 위와 같은 상황이 아니라면 익명 클래스보다는 람다를 사용하는 것을 권장하는 게 맞는 거 같다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.