add user agent parser and update build configuration

This commit is contained in:
2025-03-11 11:32:36 +09:00
parent 3db3499d30
commit 09731f464d
47 changed files with 8825 additions and 18 deletions

163
docs/EqualsAndHashCode.md Normal file
View File

@@ -0,0 +1,163 @@
# **자바의 `equals()` 와 `hashCode()` 메서드 완벽 정리**
## **1. `equals()` 와 `hashCode()`란?**
자바에서 객체를 비교할 때, `==` 연산자는 **메모리 주소를 비교**한다.
하지만 **객체의 내용이 같은지를 비교**하려면 `equals()`를 재정의해야 한다.
또한, **HashMap, HashSet 같은 컬렉션에서 객체를 올바르게 저장하고 검색하려면 `hashCode()`도 재정의**해야 한다.
---
## **2. `equals()` 와 `hashCode()`의 기본 동작**
### ✅ **1. `equals()` 기본 구현 (`Object` 클래스)**
모든 클래스는 `Object` 클래스를 상속받으며, `Object`의 기본 `equals()`는 **메모리 주소를 비교**한다.
```java
public boolean equals(Object obj) {
return (this == obj);
}
```
즉, 기본적으로 `equals()``==`과 동일한 동작을 한다.
### ✅ **2. `hashCode()` 기본 구현 (`Object` 클래스)**
`Object``hashCode()`는 **객체의 메모리 주소를 기반으로 해시 값을 반환**한다.
```java
public native int hashCode();
```
(※ `native`는 자바가 아닌 C/C++로 구현되었다는 의미)
---
## **3. `equals()`와 `hashCode()`를 함께 재정의해야 하는 이유**
**예제:** `HashSet`에서 `equals()`만 재정의했을 때 발생하는 문제
```java
import java.util.HashSet;
import java.util.Objects;
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // false (올바르게 동작하지 않음!)
}
}
```
✔ 이유: `HashSet`**`hashCode()`를 먼저 비교한 후 `equals()`를 실행**하는데, `hashCode()`가 재정의되지 않아서 `contains()`가 실패한다.
✅ 해결 방법: `hashCode()``equals()`와 함께 재정의한다.
---
## **4. `equals()`와 `hashCode()` 올바르게 재정의하는 방법**
### ✅ **올바른 `equals()` 재정의**
1. **반사성** (`x.equals(x) == true`)
2. **대칭성** (`x.equals(y) == true``y.equals(x) == true`)
3. **추이성** (`x.equals(y) == true` & `y.equals(z) == true``x.equals(z) == true`)
4. **일관성** (`x.equals(y)`의 결과가 변하지 않음)
5. **null 비교 시 `false` 반환** (`x.equals(null) == false`)
**예제 코드:**
```java
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
```
---
### ✅ **올바른 `hashCode()` 재정의**
**같은 객체는 같은 `hashCode()`를 가져야 한다.**
**다른 객체라도 같은 `hashCode()`를 가질 수는 있지만 가능하면 충돌을 줄이는 것이 좋다.**
**예제 코드:**
```java
@Override
public int hashCode() {
return Objects.hash(name);
}
```
`Objects.hash()``null` 체크까지 포함된 안전한 방식이다.
---
## **5. `equals()`와 `hashCode()`를 올바르게 구현한 예제**
```java
import java.util.HashSet;
import java.util.Objects;
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // true (올바르게 동작!)
}
}
```
`equals()``hashCode()`를 함께 재정의했기 때문에 `contains()`가 정상적으로 동작한다.
---
## **6. `record`를 사용하면 자동 구현됨! (Java 14+)**
자바 14부터 도입된 `record``equals()``hashCode()`를 자동 생성해준다.
```java
record Person(String name) {}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // true
}
}
```
`record`는 **자동으로 `equals()``hashCode()`를 올바르게 구현**해준다.
---
## **7. 결론**
- **`equals()`만 재정의하면 HashSet, HashMap 등의 컬렉션에서 문제가 발생할 수 있다.**
- **`hashCode()`도 함께 재정의해야 컬렉션에서 정상 동작한다.**
- **`record`를 사용하면 자동으로 `equals()``hashCode()`가 구현된다.**
이제 `equals()``hashCode()`를 제대로 이해했으니, 컬렉션을 사용할 때 예상치 못한 버그를 피할 수 있을 것이다!