equals를 재정의하려거든 hashCode도 재정의하라 equals 재정의 후 hashCode를 재정의해야 한다. 그 이유는 논리적으로 같은 객체는 같은 해시코드를 반환해야하기 때문이다. Hash 함수를 사용하는 컬렉션을 사용하지 않으면 hashCode를 재정의하지 않더라도 문제가 발생하지 않는다. 하지만 애플리케이션 레벨에서 바라봤을 때 해시 컬렉션을 사용하지 않는다고 확신하기 어렵다. 따라서 hashCode도 같이 재정의 하는 것이 필요하다. equals만 재정의 한 경우 import java.util.*; public class Car { private final String name; public Car(String name) { this.name = name; } @Override public..
equals는 일반 규약을 지켜 재정의하라 Object는 자바의 최상위 클래스이며, 모든 클래스의 부모 역할을 한다. Object의 메서드 중 equals는 보통 논리적 동치성 즉, String 이나 Integer 처럼 값 클래스 들의 주솟값이 아닌 값을 비교하기 위해 재정의 해서 사용한다. equals 재정의를 사용하지 않아도 되는 경우 각 인스턴스가 본질적으로 고유한 경우 Thread 처럼 값을 표현하는 것이 아닌 동작하는 개체를 표현하는 클래스는 Object 클래스의 equals 메서드에서 기본적으로 제공한다. 논리적 동치성(logical equality)을 검사할 일이 없는 경우 상위 클래스에서 재정의한 equals가 하위 클래스에도 적합한 경우 예를 들어 Set 인터페이스는 AbstractSet..
try-finally 보다는 try-with-resources를 사용하라 자바 라이브러리에는 InputStream, Connection 등 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 자원을 닫는 것은 놓치기 쉬워 예측하기 어려운 성능 문제로 이어지기도 한다. 상당 수가 안정망으로 finalizer, cleaner 등을 활용하지만 실행을 보장하지 못한다. 예외가 발생하거나 메서드에서 반환되는 경우를 포함해서 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. 다음 코드 예시를 살펴보자. try-finally static String firstLineOfFile(String path) throws IOException { BufferedReader br = new Buf..
finalizer와 cleaner 사용을 피하라 자바는 finalizer 와 cleaner 두 가지로 객체 소멸자를 제공한다. 그 중 finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. cleaner는 finalizer 보다 덜 위험하지만, 여전히 예측할 수 없고, 느리며 일반적으로 불필요하다. 따라서 Java 9 에서 부터 deprecated(사용 자제) 되었으며, 그 대안으로 cleaner를 사용한다. deprecated 관련 문서 : https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#finalize-- 정리에 앞서 상태를 영구적으로 수정하는 작업에서는 절대로 두 가지의 객체 소멸자를 사용하면 안된다..
다 쓴 객체 참조를 해제하라 C, C++ 처럼 메모리를 직접 관리하는 언어와 다르게 자바는 GC에 의해 메모리를 관리한다. 하지만 메모리 관리에 신경 써야한다. 스택을 간단히 구현한 예제 코드를 살펴보자. public class Stack { private Object[] elements; private int size; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { ..
불필요한 객체 생성을 피하라 String name = new String("sasca37"); // 매번 인스턴스 생성 String name2 = "sasca37"; // 상수풀 사용 똑같은 기능의 객체를 매번 생성하기보다 객체 하나를 재사용하는 편이 나을 때가 많다. 불변 클래스라면 (가변 클래스여도 변경의 여지가 없다면) 생성자보다 정적 팩터리 메서드를 사용하여 객체를 재사용하는 것이 효율적이다. static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); } 해당 방식의 문제는 String.matches 메서드를 사용한다는 데 있다. 이..
자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 많은 클래스가 하나 이상의 자원에 의존한다. 예를 들어 맞춤법 검사기는 사전(dictionary)이 없으면 검사를 할 수 없다. 이러한 맞춤법 검사기와 사전과의 관계를 의존 관계라고 하며, 이런 클래스를 정적 유틸 클래스로 구현한 모습을 자주 볼 수 있다. 정적 유틸 클래스를 잘못 사용한 예 import java.util.List; public class SpellChecker { private static final KoreaDictionary dictionary = ...; private SpellChecker() {}; public static boolean isValid(String word) {...}; public static List su..
인스턴스화를 막으려거든 private 생성자를 사용하라 java.lang.Math 나 java.util.Collections 처럼 단순히 정적 메서드와 정적 필드만을 담은 클래스를 만들고 싶은 경우가 있을 것이다. 해당 유틸리티 클래스들은 인스턴스로 만들어 쓰려고 설계한게 아니지만, 생성자를 명시하지 않아도 컴파일러가 자동으로 빈 생성자를 만들어준다. 추상클래스는 abstract 명시로 인스턴스화 할 수 없기 때문에 추상클래스로 해결할 수 있지만, 추상클래스를 상속하여 하위 클래스에서 인스턴스화를 한다면 해결되지 않는다. public abstract class UtilityClass { public abstract void print(); } class Child extends UtilityClass {..