From 43eaaa44373e7ccb774cc503cf6089d682d58a57 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Sat, 11 Jan 2025 10:09:16 +0900 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" index 63aad46..dd03a5f 100644 --- "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" +++ "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" @@ -109,8 +109,8 @@ Entry deepCopy(){ > 좋은 방식은 아니다. 생성자에서는 재정의될 수 있는 메서드를 호출하지 않아야 하는데 clone 메서드도 마찬가지이기 때문이다. ### 주의점 -- 상속용 클래스의 경우 loneable을 구현해서는 안됨 -- 스레드 안전 클래스 작성 시 Clneable을 구현하는 모든 클래스는 clone을 재정의해야 함(접근 제한자는 pubic, 반환 타입은 클래스 자신) +- 상속용 클래스의 경우 Cloneable을 구현해서는 안됨 +- 스레드 안전 클래스 작성 시 Cloneable을 구현하는 모든 클래스는 clone을 재정의해야 함(접근 제한자는 public, 반환 타입은 클래스 자신) ### 가능한 다른 선택지! 변환 생성자/ 변환 팩터리 From 97c0c993994def9f01f3745058e6763662318725 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:54:04 +0900 Subject: [PATCH 2/9] feat: Add item 11.md --- ...04\355\226\211\355\225\230\353\235\274.md" | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" new file mode 100644 index 0000000..1b07c96 --- /dev/null +++ "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" @@ -0,0 +1,86 @@ +## item 11 + +### equals를 재정의하려거든 hashCode도 재정의하라 + +--- + +### 🙋‍♀️ 왜 함께 재정의해줘야 하나요? + +`hashCode` 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 `HashMap`이나 `HashSet` 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이기 때문입니다. + +--- + +### 🙋‍ hashCode 의 일반 규약은 무엇인가요? +다음은 규약 중 일부입니다. +#### 1. equals 비교에 사용되는 핵심 필드가 달라지지 않았다면, hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 합니다. +#### 2. equals 비교를 통해 같다고 판명된 객체라면, 각 객체의 hashCode는 똑같은 값을 반환해야 합니다. +#### 3. equals 비교를 통해 다르다고 판단된 값이라도, 서로 다른 값을 반환할 필요는 없습니다. 다만!! 다른 객체라면 다른 값을 반환하는 게 해시테이블의 성능이 좋아집니다. + +--- + +### 🙌 hashCode 재정의를 잘못한다면 2번 조항에서 문제가 생깁니다. +간단히 말하자면, `equals`에서는 같은 객체라고 판단했으나 `hashCode`에 대한 재정의가 잘못되어서 `HashMap`이나 `HashSet`에서 같은 원소로 판별되지 않는 문제가 생긴다는 것입니다. + +`equals`는 물리적으로 다른 두 객체를 논리적으로 같은 객체라고 판단할 수 있지만 `hashCode`를 재정의하지 않는다면 `hashCode`는 이를 모를 것이기 때문입니다. + +--- + +### 🙋‍♀️ 그럼 hashCode는 어떻게 구현하는 게 올바른 방법인가요? + +#### 최악의 방법 +```java +@Override +public int hashCode(){ + return 42; +} +``` +`동치인` 모든 객체에서 똑같은 해시코드를 반환하기에 적법합니다. +문제는 `동치가 아닌` 객체에서도 똑같은 해시코드를 반환하기 때문에 생깁니다. +결과적으로 해시테이블의 버킷 하나에 모두 담겨 연결 리스트처럼 동작하고, 평균 수행 시간이 0(1) > O(n)으로 느려져서 객체가 많아지면 도저히 쓸 수 없게 됩니다. + +--- + +#### 좋은 방법 +```java +@Override +public int hashCode(){ + int result = Short.hashCode(areaCode); + result = 31 * result + Short.hashCode(prefix); + result = 31 * result + Short.hashCode(lineNum); + return result; +} +``` +좋은 해시 함수라면 서로 다른 인스턴스에 다른 해시코드를 반환합니다.(3번 조항) +##### 1) int 변수 result를 선언한 후 `첫번째 핵심 필드`를 단계 `2.a 방식으로 계산`한 해시코드로 초기화합니다. +##### 2) 해당 객체의 나머지 핵심 필드에 대한 작업을 수행합니다. +| 필드 형식 및 조건 | 처리 방식 | +|--------------------------|------------------------------------------------------------------------------| +| 기본 타입 | `Type.hashCode(f)` | +| 참조 타입 + equals 재귀 호출 처리 | 해당 필드의 `hashCode` 재귀 호출 / 필드의 표준형 제작 | +| 배열 | 배열의 참조값이 아닌 각 핵심 원소에 대한 값을 처리하며 갱신(누적) / 모든 원소가 핵심 원소라면, Arrays.hashCode를 사용 | + +#### 3) 계산한 해시코드로 result 갱신 +#### 4) result 반환 +#### 5) 동치인 인스턴스에 대해 똑같은 해시 코드를 반환하는지 테스트 +#### 6) 단위 테스트 작성 + +--- + +✔ 추가 주의점 +#### 1) null, 핵심 원소 X 배열 > 상수, 특히 0으로 처리하기 +#### 2) 파생 필드는 계산에서 제외하기 +#### 3) equals 비교에 사용되지 않은 필드 반드시 제외하기 +#### 4) result와 곱하는 수는 짝수이거나 오버플로가 발생하면 안됨(31 추천) +#### 5) 성능 개선 때문에 핵심 필드 생략을 해선 안됨, 되레 속도가 느려질 가능성 있음 + +--- + +✔ 추가 개선 사항 +#### 1) 코드를 줄이기 위해 hash 메서드를 사용할 수 있으나 성능에 민감하지 않은 상황에서만 사용하기 +#### 2) 클래스가 불변, 해시코드를 계산하는 비용이 크다면 캐싱을 고려(해당 타입의 객체가 해시의 키로 사용) +#### 3) 해시의 키로 사용되지 않는다면,지연 초기화 전략 사용 +#### 4) hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세히 공표하지 말기 + +이에 따라서 핵심 필드 3개만을 사용하여 간단히 계산이 가능해졌습니다. + +--- From e29e74fc21d7d9680ac3f03e349cf7a1348c5bbc Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:55:51 +0900 Subject: [PATCH 3/9] feat: Add item 23.md --- ...34\354\232\251\355\225\230\353\235\274.md" | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 "4\354\236\245_\355\201\264\353\236\230\354\212\244\354\231\200_\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/\354\225\204\354\235\264\355\205\234 23/\355\203\234\352\267\270_\353\213\254\353\246\260_\355\201\264\353\236\230\354\212\244\353\263\264\353\213\244\353\212\224_\355\201\264\353\236\230\354\212\244_\352\263\204\354\270\265\352\265\254\354\241\260\353\245\274_\355\231\234\354\232\251\355\225\230\353\235\274.md" diff --git "a/4\354\236\245_\355\201\264\353\236\230\354\212\244\354\231\200_\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/\354\225\204\354\235\264\355\205\234 23/\355\203\234\352\267\270_\353\213\254\353\246\260_\355\201\264\353\236\230\354\212\244\353\263\264\353\213\244\353\212\224_\355\201\264\353\236\230\354\212\244_\352\263\204\354\270\265\352\265\254\354\241\260\353\245\274_\355\231\234\354\232\251\355\225\230\353\235\274.md" "b/4\354\236\245_\355\201\264\353\236\230\354\212\244\354\231\200_\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/\354\225\204\354\235\264\355\205\234 23/\355\203\234\352\267\270_\353\213\254\353\246\260_\355\201\264\353\236\230\354\212\244\353\263\264\353\213\244\353\212\224_\355\201\264\353\236\230\354\212\244_\352\263\204\354\270\265\352\265\254\354\241\260\353\245\274_\355\231\234\354\232\251\355\225\230\353\235\274.md" new file mode 100644 index 0000000..fc41798 --- /dev/null +++ "b/4\354\236\245_\355\201\264\353\236\230\354\212\244\354\231\200_\354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/\354\225\204\354\235\264\355\205\234 23/\355\203\234\352\267\270_\353\213\254\353\246\260_\355\201\264\353\236\230\354\212\244\353\263\264\353\213\244\353\212\224_\355\201\264\353\236\230\354\212\244_\352\263\204\354\270\265\352\265\254\354\241\260\353\245\274_\355\231\234\354\232\251\355\225\230\353\235\274.md" @@ -0,0 +1,233 @@ +## item 23 + +### 태그 달린 클래스보다는 클래스 계층구조를 활용하라 + +--- + +### 🙋‍♀️ `태그 달린 클래스`는 무엇인가요? + +```java +import java.awt.Shape; + +class Figure { + enum Shape {RECTANGLE, CIRCLE} + + ; + + // 태그 필드 - 현재 모양을 나타낸다. + final Shape shape; + + // 다음 필드들은 모양이 사각형(RECTANGLE)일 때만 쓰인다. + double length; + double width; + + // 다음 필드는 모양이 원(CIRCLE)일 때만 쓰인다. + double radius; + + // 다음 필드는 공통적으로 사용된다. + final double x; + final double y; + + // 원용 생성자 + Figure(double x, double y, double radius) { + shape = Shape.CIRCLE; + this.x = x; + this.y = y; + this.radius = radius; + } + + // 사각형용 생성자 + Figure(double x, double y,double length, double width) { + shape = Shape.RECTANGLE; + this.x = x; + this.y = y; + this.length = length; + this.width = width; + } + + // Shape의 종류와 관계 없이 현재 위치 반환하는 메서드 + String getFormattedPosition() { + return String.format("현재 위치는 %.0f, %.0f 입니다.", x, y); + } + + double area() { + switch (shape) { + case RECTANGLE: + return length * width; + case CIRCLE: + return Math.PI * (radius * radius); + default: + throw new AssertionError(shape); + } + } +} +``` + + +태그가 달린 클래스는 `두 가지 이상의 의미를 표현`할 수 있으며 현재 `표현하는 의미를 태그 값`으로 알려주는 클래스입니다. + +--- + +### 🙋‍♀️ 위의 예제의 단점은 무엇인가요? + +장황하고, 오류를 내기 쉽고, 비효율적입니다. + +#### 1) `두 가지 이상의 의미를 하나의 클래스에서 표현`하기 위해 `쓸데없는 코드`가 많습니다. > 가독성과 메모리 차지 ↑ + +#### 2) `필드를 final 로 선언하려면` 해당하는 의미에 `쓰이지 않는 필드들까지 초기화`가 필요합니다. > 컴파일러 에러 인지 X / 런타임 에러 발견 가능성 ↑ + +#### 3) `새로운 의미 추가`를 위해 `root 클래스에 불필요한 코드를 추가`해야 합니다. > 컴파일러 에러 인지 X / 런타임 에러 발견 가능성 ↑ + +#### 4) `Figure` 라는 인스턴스의 타입만으로는 `현재 나타내는 의미(RECTANCLE, CIRCLE, ..)`를 알 길이 없음 > 가독성과 코드 이해도 ↓ + +--- + +### 🙌 태그가 달린 클래스를 대체하는 것, 서브타이핑 + +이런 상황에서 `타입 하나`로 `다양한 의미의 객체를 표현`하는 훨씬 나은 수단을 제공하는데, +이가 `클래스 계층구조를 활용`하는 `서브 타이핑`입니다. +`서브 타이핑` 이란, 하위 타입이 상위 타입을 대체할 수 있다는 개념으로, +`리스코프 치환 원칙`에 따라 동작합니다. + +계층 구조라고 하는 것은 코드를 보면 쉽게 이해할 수 있습니다. +현재 구현된 것과 상속된 것을 정리해보면 `Figure > Rectangle > Square` 로 계층 구조를 이룹니다. + +```java +import java.awt.Shape; + +abstract class Figure { + final double x; // x 좌표 + final double y; // y 좌표 + + // 생성자: x와 y 좌표 초기화 + Figure(double x, double y) { + this.x = x; + this.y = y; + } + + // 추상 메서드: 면적 계산 + abstract double area(); + + // 현재 위치 반환하는 일반 메서드 + String getFormattedPosition() { + return String.format("현재 위치는 %.0f, %.0f 입니다.", x, y); + } +} +``` + +```java +class Rectangle extends Figure { + final double length; + final double width; + + Rectangle(double x, double y, double length, double width) { + super(x, y); // 부모 클래스 생성자 호출 + this.length = length; + this.width = width; + } + + @Override + double area() { + return length * width; + } +} +``` + +```java +class Square extends Rectangle { + Square(double x, double y, double side) { + super(x, y, side, side); // 부모 클래스 생성자 호출 + } +} +``` + +위의 클래스를 아래처럼 사용하면, +`Figure` 클래스 하나로 `Rectangle`, `Circle`, `Square` 등 다양한 의미의 객체를 표현할 수 있습니다. + +```java +public static void main(String[] args) { + Figure rectangle = new Rectangle(5, 10); + Figure square = new Square(5); + + System.out.println(rectangle.area()); // 50.0 + System.out.println(square.area()); // 25.0 +} +``` + +--- + +### ✅ `태그 달린 클래스` > `클래스 계층구조`로 리팩토링하는 방법! + +위의 예제 코드는 태그 달린 클래스였던 `Figure` 클래스를 리팩토링한 것입니다. +그럼 어떻게 할 수 잇는지 알아보겠습니다. + +### 추상 클래스 정의 +#### 1) 계층구조의 root가 될 추상 클래스를 정의 [Figure abstract class] +#### 2) 태그 값에 따라 동작이 달라지는 메서드를 추상 메서드로 정의 [Figure > area abstract method] +#### 3) 태그 값에 따라 동작이 달라지지 않는 메서드를 일반 메서드로 추가 [Figure > getFormattedPosition method] +#### 4) 하위 클래스에서 공통으로 사용하는 데이터 필드 루트 클래스로 추가 [Figure > x, y final field] + +
📑 완성된 코드 + +```java +import java.awt.Shape; + +abstract class Figure { + final double x; // x 좌표 + final double y; // y 좌표 + + // 생성자: x와 y 좌표 초기화 + Figure(double x, double y) { + this.x = x; + this.y = y; + } + + // 추상 메서드: 면적 계산 + abstract double area(); + + // 현재 위치 반환하는 일반 메서드 + String getFormattedPosition() { + return String.format("현재 위치는 %.0f, %.0f 입니다.", x, y); + } +} +``` + +
+ +
+ +### 구체 클래스 정의 +#### 1) 의미별로 루트 클래스를 확장한 구체클래스 정의 [Rectangle, Circle, ...] +#### 2) 각자 의미에 맞는 데이터 필드 추가 [Rectangle > length, width, Circle > radius] +#### 3) 추상 메서드 구현 [area abstract method overriding] + +
📑 완성된 코드 + +```java +class Rectangle extends Figure { + final double length; + final double width; + + Rectangle(double x, double y, double length, double width) { + super(x, y); // 부모 클래스 생성자 호출 + this.length = length; + this.width = width; + } + + @Override + double area() { + return length * width; + } +} +``` + +
+ +--- + +### 🙌 달라진 점 +#### 1) `관련 없던 데이터 필드 모두 제거`됨 (살아남은 필드는 모두 final) +#### 2) 런타임 오류가 발생하기 전에 추상 메서드 구현을 `컴파일러가 확인`해줌 (switch 문에 case 미포함으로 인한 에러 발생 X) +#### 3) 루트 클래스의 코드에 접근하지 않고도 `독립적으로 계층구조 확장이 가능` +#### 4) `타입이 의미 별로 존재`하기에 변수 의미 명시 및 제한, 변수에 따른 의미 별 매겨변수 설정 가능 +#### 5) `타입 사이 자연스런 계층 관게 반영 가능` > 유연성 + 컴파일타임 타입 검사 능력 ↑ (ex Square 클래스) From fb874878ab274bd928464bf8b5b01e9625f9b84d Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:59:50 +0900 Subject: [PATCH 4/9] refactor: Fix file name --- ...54\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" => "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" (100%) diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" similarity index 100% rename from "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" rename to "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" From cab87165ee25d3ccab1c8abe76a74b8984426d80 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:59:55 +0900 Subject: [PATCH 5/9] feat: Add item 17.md --- ...14\355\231\224\355\225\264\353\235\274.md" | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" new file mode 100644 index 0000000..d486b10 --- /dev/null +++ "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" @@ -0,0 +1,139 @@ +## item 17 + +### 변경 가능성을 최소화하라 + +--- + +### 🙋‍♀️ 왜 변경 가능성을 최소화하는 게 좋은가요? +변경 가능성이 최소화된 클래스를 `불변 클래스`라고 합니다. 이는 인스턴스 내부 값을 수정할 수 없는 클래스로, 객체가 파괴되는 순간까지 절대 달라지지 않습니다. + +이런 클래스를 지향해야 하는 이유는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, +오류가 생길 여지도 적고, 안전하기 때문입니다. + +#### 1) 상대적으로 설계,구현,사용이 쉬움 +- 불변: + - 프로그래머의 노력 없이도 영원히 불변으로 남음 + - 값이 바뀌지 않는 상태이기에 구조가 아무리 복잡해도 불변식을 유지하기 쉬움 (실패 원자성) + - 불변 객체인 경우 불변 객체끼리는 내부 데이터 공유도 가능함 +- 가변: 메서드에 의해 임의의 복잡한 상태에 놓일 수 있음 + - 따라서 가변 객체 사용시에는 클래스의 보안을 지키기 위해 진짜 해당 객체인지 확인해야 함 + +#### 2) 스레드 안전 +- 근본적으로 스레드 안전하여 따로 동기화할 필요 X +- 따라서 안심하고 공유가 가능하고, 그렇기에 최대한 재활용하는 게 좋음 +- 상수로 선언하거나, 정적 팩터리를 통해 생성하여 중복 생성을 피하고, 메모리 사용량/가비지 컬렉션 비용을 줄일 수 있음 +- 결국 추후에 나올 `방어적 복사`도 필요 없게 만듦(1번 장점에 해당) + +--- + +### 🙋‍♀️ 불변 클래스는 어떻게 만들 수 있나요? + +아래의 다섯 가지 규칙을 잘 따르면 됩니다. +#### 1) 객체의 상태를 변경하는 메서드를 제공하지 않는다. +#### 2) 클래스를 확장할 수 없도록 한다. 하위 클래스에서 객체의 상태를 변하게 하는 것을 막아준다. (final 클래스로 정의 / 생성자를 private, protected로 정의) +#### 3) 모든 필드를 `final`로 선언한다. 불변으로 명시적으로 선언하여 설계자의 의도를 명확히 들어낸다. +#### 4) 모든 필드를 `private`로 선언한다. 필드가 참조하는 가변 객체를 클라이언트에서 직접 접근하여 수정하는 일을 막아준다. `public`으로 정의한 경우, 내부 표현을 쉽게 바꾸지 못하기에 `private`을 추천한다. +#### 5) 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다. 클래스에 가변 객체를 참조하고 있는 필드가 있다면, 클라이언트에서 그 객체를 변경할 수 있다. +
✅ 5번의 해결책 방어적 복사 + +1) `List` 가변 객체를 참조하는 필드가 있는 클래스에서, 해당 객체를 그대로 반환하고 있습니다. +```java +public final class ImmutableClass { + private final List items; + + public ImmutableClass(List items) { + this.items = items; // 가변 리스트를 직접 참조 + } + + // 가변 객체를 직접 반환 + public List getItems() { + return items; // 외부에서 이 리스트를 수정할 수 있음 + } +} +``` + +2) 이 과정에서 문제가 발생합니다. 외부에서도 해당 객체의 내부 필드에 직접 접근하여 수정이 가능해지기 때문에 불변성을 보장받지 못하기 때문입니다. +```java +public static void main(String[] args) { + List list = new ArrayList<>(); + list.add("item1"); + ImmutableClass immutable = new ImmutableClass(list); + + // items 리스트를 직접 수정할 수 있음 + List retrievedList = immutable.getItems(); + retrievedList.add("item2"); // 외부에서 리스트 수정 가능 + System.out.println(retrievedList); // [item1, item2] + + // 원래 리스트도 수정됨 + System.out.println(immutable.getItems()); // [item1, item2] +} +``` + +3) 따라서 `방어적 복사`라는 것을 수행하라고 합니다. 가변 객체의 복사본을 저장하여 반환하는 방식입니다. +```java +public final class ImmutableClass { + private final List items; + + public ImmutableClass(List items) { + this.items = new ArrayList<>(items); // 가변 리스트의 복사본을 저장 + } + + // 가변 객체의 복사본을 반환 + public List getItems() { + return new ArrayList<>(items); // 외부에서 수정할 수 없도록 복사본 반환 + } +} + +``` + +4) 이렇게 되면 아까와 달리 외부에서 직접 수정이 가능하지만, 이가 내부 필드에는 영향을 미치지 않게 되어 불변성이 보장됩니다. +```java +public static void main(String[] args) { + List list = new ArrayList<>(); + list.add("item1"); + ImmutableClass immutable = new ImmutableClass(list); + + // items 리스트를 직접 수정할 수 있음 + List retrievedList = immutable.getItems(); + retrievedList.add("item2"); // 외부에서 리스트 수정 가능 + System.out.println(retrievedList); // [item1, item2] + + // 원래 리스트는 수정되지 않음 + System.out.println(immutable.getItems()); // [item1] +} +``` + +
+ +--- + +### 🙌 함수형 프로그래밍을 통한 불변 클래스 제작 +제목과 같이 함수형 프로그래밍을 통한 불변 클래스를 제작할 수 있다. + +
✅함수형 프로그래밍과 절차적/명령형 프로그래밍의 차이 + +#### 함수형 프로그래밍 +- 인스턴스 자신은 수정하지 않고 새로운 인스턴스를 만들어 반환하는 방식을 사용한 것으로, +실제 필드에는 영향이 없어 불변을 유지하는 프로그래밍 패턴 +- 불변이라는 점을 강조하기 위해 메서드 이름에도 동사 대신 전치사를 사용 + +#### 절차적/명령형 프로그래밍 +- 인스턴스 자신을 수정하여 자신의 상태를 변화시키는 방식을 사용한 것으로, +- 실제 필드에 영향이 있기에 불변을 유지하기는 어려운 프로그래밍 패턴 + +
+ +### 🙋‍♀️ 그럼 불변 클래스의 단점은 없나요? +다만 가변에 비해 성능(시간, 공간 문제)이 떨어지는 문제가 생길 수 있습니다. +원하는 객체를 완성하기까지의 단계가 많고, 그 중간 단계에서 만들어진 객체들이 모두 버려진다면 성능 문제가 더 불거지게 됩니다. + +#### 1) 다단계 연산들을 예측하여 기본 기능으로 제공 (private 가변 동반 클래스를 활용) +#### 2) 예측이 어렵다면, public 가변 동반 클래스로 제공 + +하여 해당 단점을 보완해볼 수 있다. + +### 정리해보자면, +1) 클래스는 꼭 필요한 경우가 아니라면 불변이어야 합니다. +2) 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄여야 합니다. + 3) 다른 합당한 이유가 없다면 모두 `priavte final`을 사용하는 게 좋습니다. +4) 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 합니다. From c9fbc77dac646610611b61cffbb957ad61e9db88 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:04:44 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor:=20=EC=96=B4=EB=AF=B8=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC=20=EB=B0=8F=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...34\354\206\214\355\231\224\355\225\264\353\235\274.md" | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" index d486b10..5790e56 100644 --- "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" +++ "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 17/\353\263\200\352\262\275_\352\260\200\353\212\245\354\204\261\354\235\204_\354\265\234\354\206\214\355\231\224\355\225\264\353\235\274.md" @@ -30,7 +30,7 @@ 아래의 다섯 가지 규칙을 잘 따르면 됩니다. #### 1) 객체의 상태를 변경하는 메서드를 제공하지 않는다. -#### 2) 클래스를 확장할 수 없도록 한다. 하위 클래스에서 객체의 상태를 변하게 하는 것을 막아준다. (final 클래스로 정의 / 생성자를 private, protected로 정의) +#### 2) 클래스를 확장할 수 없도록 한다. 하위 클래스에서 객체의 상태를 변하게 하는 것을 막아준다. (final 클래스로 정의 / 생성자를 private, package-private 정의) #### 3) 모든 필드를 `final`로 선언한다. 불변으로 명시적으로 선언하여 설계자의 의도를 명확히 들어낸다. #### 4) 모든 필드를 `private`로 선언한다. 필드가 참조하는 가변 객체를 클라이언트에서 직접 접근하여 수정하는 일을 막아준다. `public`으로 정의한 경우, 내부 표현을 쉽게 바꾸지 못하기에 `private`을 추천한다. #### 5) 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다. 클래스에 가변 객체를 참조하고 있는 필드가 있다면, 클라이언트에서 그 객체를 변경할 수 있다. @@ -108,7 +108,7 @@ public static void main(String[] args) { --- ### 🙌 함수형 프로그래밍을 통한 불변 클래스 제작 -제목과 같이 함수형 프로그래밍을 통한 불변 클래스를 제작할 수 있다. +제목과 같이 함수형 프로그래밍을 통한 불변 클래스를 제작할 수 있습니다.
✅함수형 프로그래밍과 절차적/명령형 프로그래밍의 차이 @@ -130,9 +130,9 @@ public static void main(String[] args) { #### 1) 다단계 연산들을 예측하여 기본 기능으로 제공 (private 가변 동반 클래스를 활용) #### 2) 예측이 어렵다면, public 가변 동반 클래스로 제공 -하여 해당 단점을 보완해볼 수 있다. +하여 해당 단점을 보완해볼 수 있습니다. -### 정리해보자면, +### 🙌 정리해보자면, 1) 클래스는 꼭 필요한 경우가 아니라면 불변이어야 합니다. 2) 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄여야 합니다. 3) 다른 합당한 이유가 없다면 모두 `priavte final`을 사용하는 게 좋습니다. From 34a1e6abe7d7bc4fc3f0535a9df2cb8f31adc8f7 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Sat, 18 Jan 2025 00:02:29 +0900 Subject: [PATCH 7/9] feat: Add item 12.md --- ...25\354\235\230\355\225\230\353\235\274.md" | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 12/toString\354\235\200_\355\225\255\354\203\201_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 12/toString\354\235\200_\355\225\255\354\203\201_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 12/toString\354\235\200_\355\225\255\354\203\201_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" new file mode 100644 index 0000000..b161067 --- /dev/null +++ "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 12/toString\354\235\200_\355\225\255\354\203\201_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" @@ -0,0 +1,67 @@ +## item 12 + +### toString을 항상 재정의하라 + +--- + +### 🙋‍♀️ 왜 재정의하는 게 좋은가요? + +기본적인 `Object`의 `toString` 메서드를 사용한다면, 우리가 원하는 대로 문자열을 보통 반환하지 않는 다는 것을 알 수 있습니다. +`PhoneNumber@abdbd` 처럼 `클래스_이름@16진수로_표시한_해시코드`를 반환할 뿐입니다. + +그래서 우리는 `toString` 일반 규약에 따라 `간결하면서 사람이 읽기 쉬운 형태의 유익한 정보`를 반환해줘야 합니다. +또한 이는 `모든 하위 클래스에서 이 메서드를 재정의하라` 라고 표현될만큼 매우 강조되고 있습니다. + +결국 재정의를 하게 되면, 해당 클래스를 사용할 때`즐겁고, 디버깅하기 쉽`습니다. + +#### 예시 1) 오류 메세지 로깅 +우리가 작성한 객체를 참조하는 컴포넌트가 오류 메세지를 로깅할 때 `toString`은 자동으로 호출될 수 있습니다. +이때 보다 쉽게 파악하기 위해 `toString`을 정의해둔다면 `보다 의미있는 메세지를 얻어갈 것`이기에 재정의하는 게 좋습니다. + +대부분 프로그래머들은 진단 메시지를 +```java +System.out.println(phoneNumber + "에 연결할 수 없습니다."); +``` +해당 방식으로 작성하게 됩니다. 재정의를 하지 않았다면 의미없는 메세지를 보게 되는 것입니다. + +#### 예시 2) 해당 객체를 포함하는 상황에서 출력을 진행 +이 객체를 포함하는 상황(ex `map 객체`)에 출력을 할 때 보다 의미있게 쓰이게 됩니다. +`{Jenny=PhoneNumber@abdbd}`가 아닌 `{Jenny=707-867-5309}` 라는 의미있는 정보를 받게 될테니 말입니다. + +--- + +### 🙋‍♀️ 그렇다면 어떻게 toString을 재정의해야 하나요? + +#### 1) 객체가 가진 주요 정보 모두를 반환하는 게 좋습니다. +#### 2) `객체가 거대`하거나 `객체의 상태를 문자열로 표현하는 게 적합하지 않`다면 요약 정보를 담아서 합니다. +#### 3) 반환값을 문서화할지 정합니다. + +--- + +### 🙋‍♀️ 문서화할지 어떻게 정하나요? + +#### 1) 문서화의 장점 +표준적이고, 명확하고, 사람이 읽을 수 있게 됩니다. +그렇기에 `그대로 입출력에 사용`하거나 `데이터 객체로 저장`하며 사용할 수 있습니다. + +따라서 문서화를 할것이라면, 명시한 포맷에 맞는 문자열과 객체를 상호 전환할 수 있는 +`정적 팩터리`나 `생성자`를 함께 제공해주면 좋습니다. + +#### 2) 문서화의 단점 +따라서 당연히 단점은 평생 그 포맷에 얽히게 된다는 점입니다. + +#### 결론 +그렇기에 명시하던 아니던, 의도는 명확히 밝혀야 합니다. +포맷을 명시하려면, 아주 정확히, 포맷을 명시하지 않기로 했다면 `형식은 정해지지 않았으며 향후 변경될 수 있다`라는 문구를 추가해 명확히 달라질 수 있음을 이야기해야한다. + +### 🙌 주의점 + +#### 1) 문서화 여부와 상관없이 toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하는 게 좋습니다. +포맷이 되었다고 해서 해당 값에 접근할 수 있는 `getter` 가 없어서 `toString`에서 값을 파싱하는 건 성능이 나빠지고 필요하지도 않은 작업입니다. +향후 포맷이 변경되면 이는 시스템이 망가지는 결과를 초래하기에 `getter`를 지원해주는 게 중요합니다. + +#### 2) 정적 유틸리티 클래스는 toString 제공할 필요 X +#### 3) 대부분의 열거 타입도 자바가 이미 완벽한 toString 제공하니 따로 제공할 필요 X +#### 4) 상위 클래스에서 이미 잘 재정의된 toString 이 있다면 제공할 필요 X +#### 5) 다만!! 하위 클래스들이 공유해야할 문자열 표현이 있는 추상 클래스에선 정의 필요!! +#### 6) AutoValue 프레임워크는 toString을 생성해주지만, 클래스의 의미가 중요한 경우 사용하지 않는 것이 좋음(ex PhoneNumber는 형식에 맞춰 반환 필요 / Potion은 자동 생성에 적합) From 47e29b4b9e21f090851f30e3707462f3eabe32c9 Mon Sep 17 00:00:00 2001 From: yerim <97941141+yerim123456@users.noreply.github.com> Date: Sat, 18 Jan 2025 00:03:12 +0900 Subject: [PATCH 8/9] remove: Delete item 11.md --- ...25\354\235\230\355\225\230\353\235\274.md" | 86 ------------------- 1 file changed, 86 deletions(-) delete mode 100644 "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" deleted file mode 100644 index 1b07c96..0000000 --- "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 11/equals\353\245\274_\354\236\254\354\240\225\354\235\230\355\225\230\353\240\244\352\261\260\353\223\240_hashCode\353\217\204_\354\236\254\354\240\225\354\235\230\355\225\230\353\235\274.md" +++ /dev/null @@ -1,86 +0,0 @@ -## item 11 - -### equals를 재정의하려거든 hashCode도 재정의하라 - ---- - -### 🙋‍♀️ 왜 함께 재정의해줘야 하나요? - -`hashCode` 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 `HashMap`이나 `HashSet` 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이기 때문입니다. - ---- - -### 🙋‍ hashCode 의 일반 규약은 무엇인가요? -다음은 규약 중 일부입니다. -#### 1. equals 비교에 사용되는 핵심 필드가 달라지지 않았다면, hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 합니다. -#### 2. equals 비교를 통해 같다고 판명된 객체라면, 각 객체의 hashCode는 똑같은 값을 반환해야 합니다. -#### 3. equals 비교를 통해 다르다고 판단된 값이라도, 서로 다른 값을 반환할 필요는 없습니다. 다만!! 다른 객체라면 다른 값을 반환하는 게 해시테이블의 성능이 좋아집니다. - ---- - -### 🙌 hashCode 재정의를 잘못한다면 2번 조항에서 문제가 생깁니다. -간단히 말하자면, `equals`에서는 같은 객체라고 판단했으나 `hashCode`에 대한 재정의가 잘못되어서 `HashMap`이나 `HashSet`에서 같은 원소로 판별되지 않는 문제가 생긴다는 것입니다. - -`equals`는 물리적으로 다른 두 객체를 논리적으로 같은 객체라고 판단할 수 있지만 `hashCode`를 재정의하지 않는다면 `hashCode`는 이를 모를 것이기 때문입니다. - ---- - -### 🙋‍♀️ 그럼 hashCode는 어떻게 구현하는 게 올바른 방법인가요? - -#### 최악의 방법 -```java -@Override -public int hashCode(){ - return 42; -} -``` -`동치인` 모든 객체에서 똑같은 해시코드를 반환하기에 적법합니다. -문제는 `동치가 아닌` 객체에서도 똑같은 해시코드를 반환하기 때문에 생깁니다. -결과적으로 해시테이블의 버킷 하나에 모두 담겨 연결 리스트처럼 동작하고, 평균 수행 시간이 0(1) > O(n)으로 느려져서 객체가 많아지면 도저히 쓸 수 없게 됩니다. - ---- - -#### 좋은 방법 -```java -@Override -public int hashCode(){ - int result = Short.hashCode(areaCode); - result = 31 * result + Short.hashCode(prefix); - result = 31 * result + Short.hashCode(lineNum); - return result; -} -``` -좋은 해시 함수라면 서로 다른 인스턴스에 다른 해시코드를 반환합니다.(3번 조항) -##### 1) int 변수 result를 선언한 후 `첫번째 핵심 필드`를 단계 `2.a 방식으로 계산`한 해시코드로 초기화합니다. -##### 2) 해당 객체의 나머지 핵심 필드에 대한 작업을 수행합니다. -| 필드 형식 및 조건 | 처리 방식 | -|--------------------------|------------------------------------------------------------------------------| -| 기본 타입 | `Type.hashCode(f)` | -| 참조 타입 + equals 재귀 호출 처리 | 해당 필드의 `hashCode` 재귀 호출 / 필드의 표준형 제작 | -| 배열 | 배열의 참조값이 아닌 각 핵심 원소에 대한 값을 처리하며 갱신(누적) / 모든 원소가 핵심 원소라면, Arrays.hashCode를 사용 | - -#### 3) 계산한 해시코드로 result 갱신 -#### 4) result 반환 -#### 5) 동치인 인스턴스에 대해 똑같은 해시 코드를 반환하는지 테스트 -#### 6) 단위 테스트 작성 - ---- - -✔ 추가 주의점 -#### 1) null, 핵심 원소 X 배열 > 상수, 특히 0으로 처리하기 -#### 2) 파생 필드는 계산에서 제외하기 -#### 3) equals 비교에 사용되지 않은 필드 반드시 제외하기 -#### 4) result와 곱하는 수는 짝수이거나 오버플로가 발생하면 안됨(31 추천) -#### 5) 성능 개선 때문에 핵심 필드 생략을 해선 안됨, 되레 속도가 느려질 가능성 있음 - ---- - -✔ 추가 개선 사항 -#### 1) 코드를 줄이기 위해 hash 메서드를 사용할 수 있으나 성능에 민감하지 않은 상황에서만 사용하기 -#### 2) 클래스가 불변, 해시코드를 계산하는 비용이 크다면 캐싱을 고려(해당 타입의 객체가 해시의 키로 사용) -#### 3) 해시의 키로 사용되지 않는다면,지연 초기화 전략 사용 -#### 4) hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세히 공표하지 말기 - -이에 따라서 핵심 필드 3개만을 사용하여 간단히 계산이 가능해졌습니다. - ---- From d0e5d8063191965281f26a898edfb48ee0982d6c Mon Sep 17 00:00:00 2001 From: 023 Date: Mon, 20 Jan 2025 10:15:03 +0900 Subject: [PATCH 9/9] =?UTF-8?q?Rename=20clone=20=EC=9E=AC=EC=A0=95?= =?UTF-8?q?=EC=9D=98=EB=8A=94=20=EC=A3=BC=EC=9D=98=ED=95=B4=EC=84=9C=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=ED=95=98=EB=9D=BC.md=20to=20clone=5F?= =?UTF-8?q?=EC=9E=AC=EC=A0=95=EC=9D=98=EB=8A=94=5F=EC=A3=BC=EC=9D=98?= =?UTF-8?q?=ED=95=B4=EC=84=9C=5F=EC=A7=84=ED=96=89=ED=95=98=EB=9D=BC.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" => "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" (100%) diff --git "a/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" "b/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md" similarity index 100% rename from "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone \354\236\254\354\240\225\354\235\230\353\212\224 \354\243\274\354\235\230\355\225\264\354\204\234 \354\247\204\355\226\211\355\225\230\353\235\274.md" rename to "3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\354\225\204\354\235\264\355\205\234 13/clone_\354\236\254\354\240\225\354\235\230\353\212\224_\354\243\274\354\235\230\355\225\264\354\204\234_\354\247\204\355\226\211\355\225\230\353\235\274.md"