Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions 2026-06-30/src/main/java/com/survivalcoding/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.survivalcoding;

import java.util.Date;
import java.util.Objects;

public class Book implements Cloneable, Comparable<Book> {
private String title;
private Date publishDate;
Comment on lines +7 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files '2026-06-30/src/main/java/com/survivalcoding/Book.java' \
             '2026-06-30/src/test' \
             '2026-06-30/src/main/java/com/survivalcoding' | sed -n '1,200p'

echo '--- Book.java outline ---'
ast-grep outline 2026-06-30/src/main/java/com/survivalcoding/Book.java --view expanded || true

echo '--- Book.java excerpt ---'
wc -l 2026-06-30/src/main/java/com/survivalcoding/Book.java
sed -n '1,220p' 2026-06-30/src/main/java/com/survivalcoding/Book.java

echo '--- usages of Book in tests/source ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' '\bBook\b' 2026-06-30/src/main/java 2026-06-30/src/test || true

echo '--- Set/Map usage around Book ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' 'new (HashSet|HashMap|TreeSet|TreeMap)<|Set<|Map<' 2026-06-30/src/main/java 2026-06-30/src/test || true

Repository: SurvivalCodingCampus/polytech_java2

Length of output: 5266


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- BookTest outline ---'
ast-grep outline 2026-06-30/src/test/java/com/survivalcoding/BookTest.java --view expanded || true

echo '--- BookTest excerpt ---'
wc -l 2026-06-30/src/test/java/com/survivalcoding/BookTest.java
sed -n '1,260p' 2026-06-30/src/test/java/com/survivalcoding/BookTest.java

Repository: SurvivalCodingCampus/polytech_java2

Length of output: 6130


title/publishDate는 불변으로 고정하세요. equals()/hashCode()/compareTo() 기준인데 둘 다 외부에서 바뀔 수 있고, 특히 publishDate는 getter가 Date 원본을 그대로 노출합니다. HashSet/HashMap/정렬된 컬렉션에 들어간 뒤 값이 바뀌면 조회와 정렬 기준이 깨집니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@2026-06-30/src/main/java/com/survivalcoding/Book.java` around lines 7 - 8,
`Book`의 `title`과 `publishDate`가 `equals()`/`hashCode()`/`compareTo()` 기준인데 외부에서
변경될 수 있으니 불변으로 고정하세요. `Book` 클래스의 필드를 `final`로 만들고, `publishDate`는 생성 시 방어적 복사를
적용하며 getter에서도 내부 `Date` 원본을 직접 반환하지 않도록 수정하세요. 필요하면 `Book` 생성자와
`getPublishDate()`를 함께 조정해 컬렉션에 들어간 뒤 상태가 바뀌지 않게 하세요.

private String comment;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public Date getPublishDate() {
return publishDate;
}

public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}

public String getComment() {
return comment;
}

public void setComment(String comment) {
this.comment = comment;
}

@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book book)) return false;
return Objects.equals(title, book.title) && Objects.equals(publishDate, book.publishDate);
}

@Override
public int hashCode() {
int result = Objects.hashCode(title);
result = 31 * result + Objects.hashCode(publishDate);
return result;
}

@Override
public int compareTo(Book other) {
return other.publishDate.compareTo(this.publishDate);
}
Comment on lines +49 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the file and map its structure first
git ls-files '2026-06-30/src/main/java/com/survivalcoding/Book.java'
ast-grep outline 2026-06-30/src/main/java/com/survivalcoding/Book.java --view expanded || true

# Show the file with line numbers
wc -l 2026-06-30/src/main/java/com/survivalcoding/Book.java
cat -n 2026-06-30/src/main/java/com/survivalcoding/Book.java

# Search for usages/related methods in the same package
rg -n "compareTo\\(|equals\\(|publishDate|title" 2026-06-30/src/main/java/com/survivalcoding -g '*.java'

Repository: SurvivalCodingCampus/polytech_java2

Length of output: 4242


compareTo()equals()와 다른 기준을 쓰고 publishDate null도 처리하지 않습니다.
publishDate만 비교해서 같은 날짜의 서로 다른 책이 0이 될 수 있어 TreeSet/TreeMap에서 항목이 합쳐집니다. other.publishDate.compareTo(this.publishDate)는 양쪽 값이 null이면 바로 NPE도 발생합니다. title까지 포함한 null-safe 비교로 맞추세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@2026-06-30/src/main/java/com/survivalcoding/Book.java` around lines 49 - 52,
`Book.compareTo()` currently orders only by `publishDate`, which can make
different books compare as equal and also throws on null dates. Update
`compareTo(Book other)` in `Book` to use a null-safe, multi-field comparison
that includes `title` as a tiebreaker and aligns better with `equals()`. Keep
the comparison logic centralized in `Book` so `TreeSet`/`TreeMap` won’t collapse
distinct books with the same publish date.


@Override
public Book clone() {
Book result = new Book();
result.title = this.title;
result.publishDate = (Date) this.publishDate.clone();
result.comment = this.comment;
return result;
}
}
197 changes: 197 additions & 0 deletions 2026-06-30/src/test/java/com/survivalcoding/BookTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package com.survivalcoding;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.text.SimpleDateFormat;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;

class BookTest {

private Date date(String yyyyMMdd) throws Exception {
return new SimpleDateFormat("yyyy-MM-dd").parse(yyyyMMdd);
}

@Test
@DisplayName("제목과 출간일이 같으면 equals()는 true를 반환한다")
void equals_sameTitleAndDate_returnsTrue() throws Exception {
// given
Book book1 = new Book();
book1.setTitle("자바의 정석");
book1.setPublishDate(date("2024-01-01"));

Book book2 = new Book();
book2.setTitle("자바의 정석");
book2.setPublishDate(date("2024-01-01"));

// when
boolean result = book1.equals(book2);

// then
assertTrue(result);
}

@Test
@DisplayName("제목 또는 출간일이 다르면 equals()는 false를 반환한다")
void equals_differentTitleOrDate_returnsFalse() throws Exception {
// given
Book book1 = new Book();
book1.setTitle("자바의 정석");
book1.setPublishDate(date("2024-01-01"));

Book book2 = new Book();
book2.setTitle("이펙티브 자바");
book2.setPublishDate(date("2024-01-01"));

// when
boolean result = book1.equals(book2);

// then
assertFalse(result);
}

@Test
@DisplayName("같다고 판단되는 두 Book은 hashCode도 동일하다")
void hashCode_equalBooks_haveSameHashCode() throws Exception {
// given
Book book1 = new Book();
book1.setTitle("자바의 정석");
book1.setPublishDate(date("2024-01-01"));

Book book2 = new Book();
book2.setTitle("자바의 정석");
book2.setPublishDate(date("2024-01-01"));

// when
int hash1 = book1.hashCode();
int hash2 = book2.hashCode();

// then
assertEquals(hash1, hash2);
}

@Test
@DisplayName("제목과 출간일이 같은 Book을 Set에 넣으면 동일 객체로 취급되어 하나만 저장된다")
void set_sameTitleAndDateBooks_treatedAsSameObject() throws Exception {
// given
Book book1 = new Book();
book1.setTitle("자바의 정석");
book1.setPublishDate(date("2024-01-01"));

Book book2 = new Book();
book2.setTitle("자바의 정석");
book2.setPublishDate(date("2024-01-01"));

Set<Book> books = new HashSet<>();

// when
books.add(book1);
books.add(book2);

// then
assertEquals(1, books.size());
}

@Test
@DisplayName("제목과 출간일이 같은 Book을 Map의 key로 사용하면 동일 키로 취급된다")
void map_sameTitleAndDateBooks_treatedAsSameKey() throws Exception {
// given
Book book1 = new Book();
book1.setTitle("자바의 정석");
book1.setPublishDate(date("2024-01-01"));

Book book2 = new Book();
book2.setTitle("자바의 정석");
book2.setPublishDate(date("2024-01-01"));

Map<Book, String> map = new HashMap<>();

// when
map.put(book1, "첫 번째 등록");
map.put(book2, "두 번째 등록");

// then
assertEquals(1, map.size());
assertEquals("두 번째 등록", map.get(book1));
}

@Test
@DisplayName("Collections.sort()를 사용하면 출간일이 최신 순(내림차순)으로 정렬된다")
void sort_books_orderedByPublishDateDescending() throws Exception {
// given
Book oldBook = new Book();
oldBook.setTitle("오래된 책");
oldBook.setPublishDate(date("2020-01-01"));

Book newBook = new Book();
newBook.setTitle("최신 책");
newBook.setPublishDate(date("2024-01-01"));

Book midBook = new Book();
midBook.setTitle("중간 책");
midBook.setPublishDate(date("2022-01-01"));

List<Book> books = new ArrayList<>(List.of(oldBook, newBook, midBook));

// when
Collections.sort(books);

// then
assertEquals("최신 책", books.get(0).getTitle());
assertEquals("중간 책", books.get(1).getTitle());
assertEquals("오래된 책", books.get(2).getTitle());
}

@Test
@DisplayName("clone()을 호출하면 필드 값이 동일한 새로운 객체가 생성된다")
void clone_returnsBookWithSameFieldValues() throws Exception {
// given
Book original = new Book();
original.setTitle("자바의 정석");
original.setPublishDate(date("2024-01-01"));
original.setComment("좋은 책");

// when
Book cloned = original.clone();

// then
assertEquals(original.getTitle(), cloned.getTitle());
assertEquals(original.getPublishDate(), cloned.getPublishDate());
assertEquals(original.getComment(), cloned.getComment());
}

@Test
@DisplayName("clone()으로 생성된 객체는 원본과 다른 참조이다")
void clone_returnsDifferentReferenceFromOriginal() throws Exception {
// given
Book original = new Book();
original.setTitle("자바의 정석");
original.setPublishDate(date("2024-01-01"));

// when
Book cloned = original.clone();

// then
assertNotSame(original, cloned);
}

@Test
@DisplayName("clone()은 publishDate를 깊은 복사하므로, 복사본의 날짜를 변경해도 원본은 영향받지 않는다")
void clone_publishDate_isDeepCopied() throws Exception {
// given
Book original = new Book();
original.setTitle("자바의 정석");
original.setPublishDate(date("2024-01-01"));

Book cloned = original.clone();

// when
cloned.getPublishDate().setTime(0L); // 복사본의 날짜를 변경

// then
assertNotEquals(original.getPublishDate().getTime(), cloned.getPublishDate().getTime());
assertNotSame(original.getPublishDate(), cloned.getPublishDate());
}
}
Loading