본문 바로가기

effective java15

제네릭 제네릭과 가변인수를 함께 쓸 때는 신중하라. 제네릭과 가변인수는 생각보다 잘 어우러지지 않는다. 아래코드를 매개변수화 타입을 적용한다면 어떻게 될까? 제네릭은 실체화 불가 타입이기 때문에 타입 관련 정보를 적게 담고 있어 컴파일 타임에 에러를 못 낼 수 있다. 이처럼 타입 안전성이 깨지니 제네릭 varargs배열 매개변수에 저장하는 것은 안전하지 않다. static void dangerous(List... stringLists) { List intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // 힘오염 발생 String s = stringLists[0].get(0); // ClassCastException } 제네릭 .. 2022. 6. 26.
제네릭 이왕이면 제네릭 타입으로 만들라 Stack을 제네릭 타입으로 만들어 보자. public class Stack { private Object[] elements; private int size = 0; private static final int DEFALUT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFALUT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if(size == 0) throw new EmptyStackException(); Object result = elemen.. 2022. 6. 19.
제네릭 로 타입은 사용하지 말라 로 타입을 쓰게 되면 컴파일 타임에 오류를 알기 힘들다. 문법적으로 막아놓지 않았지만 절대로 쓰면 안된다. 로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다. 로 타입은 안되지만 List처럼 임의 객체를 허용하는 매개변수화 타입은 괜찮다. List에 List을 넘길 수 있지만 List는 안된다. List은 List의 하위타입이지만, List는 아니기 때문이다. 그렇다면 제네릭 타입을 쓰고 싶지만 매개변수 타입을 신경쓰고 싶지 않다면 어떻게 해야 할까? class를 사용하는 것이다. Collection에는 null이외에는 어떤 원소도 넣을 수 없다. 하지만, class 리터럴에는 로 타입을 사용해야 한다. List.class는 허용되지 않는다. 또, instanc.. 2022. 6. 18.
클래스와 인터페이스 멤버 클래스는 되도록 static으로 만들라. 중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다. 그 외의 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다. 중첩 클랫의 종류 외부 접근 쓰임 정적 멤버 클래스 가능 외부 클래스에게 도움을 줄 때 (비정적) 멤버 클래스 불가 어댑터로 정의 할 때 익명 클래스 비정적 문맥 정적 팩토리 메서드나 작은 함수를 구현 할 때 지역 클래스 비정적 문맥 정적 멤버 클래스 정적 멤버클래스는 public static class로 정의하고 private 멤버를 외부 클래스에서 접근할 수 있다는 점만 제외하고 모두 같다. 외부 클.. 2022. 6. 17.
클래스와 인터페이스 인터페이스는 구현하는 쪽을 생각해 설계하라. 자바 8이후부터는 인터페이스도 디폴트 메서드를 구현 할 수 있게 되었지만 생각 할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어렵다. removeIf의 예제를 보면 Predicate에서 true를 반환하는 것들을 제거한다. default boolean removeIf(Predicate 2022. 6. 17.
클래스와 인터페이스 추상 클래스보다는 인터페이스를 우선하라. 추상클래스는 정의 타입을 항상 하위 클래스로 하여야 한다. 하지만, 인터페이스같은 경우에는 어떤 클래스를 상속해도 같은 타입으로 취급한다. 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다. 인터페이스가 요구하는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 끝이다. 반면 추상클래스는 확장하기 어렵다. 추상 클래스를 확장하기 원한다면 상속 받는 모든 클래스의 공통조사이여야 한다. 적절하지 않은 상황에도 강제로 해야만 하는 상황이 생긴다. 인터페이스는 믹스인(mixin)정의에 안성맞춤이다. 믹스인 정의는 클래스가 구현 할 수 있는 타입으로 부모클래스가 되지 않으면서 선택적 행위를 제공해준다. Comparable은 자신을 구현한 .. 2022. 6. 14.
클래스와 인터페이스 상속보다는 컴포지션을 사용하라 상속(클래스가 다른 클래스를 확장하는)은 코드를 재사용하는 강력한 수단이지만, 항상 최선은 아니다. 패키지 경계를 넘어 다른 클래스의 구체 클래스를 상속하는 일은 위험하다. 메서드 호출과 달리 상속은 캡슐화를 깨드린다. 상위클래스가 어떻게 구현되는냐에 따라서 하위 클래스 동작에 이상이 생길 수 있다. 상위 클래스는 릴리즈마다 구현이 바뀔 수 있어 문서화를 해두고 발맞춰 수정해야만 한다. 아래 예제에서 addAll()이 c.size()를 더하고 있는데 add()메서드에서 한번 더 더하기 때문에 2배가 된다. 그렇다고 addCount를 더하는 부분을 지운다고 안심할 수 없다. 나중에 상위클래스(HashSet) 어떻게 바뀔지 모르기 때문이다. public class Instrum.. 2022. 6. 14.
클래스와 인터페이스 변경 가능성을 최소화하라 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다. 불변 클래스는 장점이 많고, 단점은 특정 상황에서의 잠재적 성능 저하뿐이다. 이것을 불변 클래스라고 부르는데 인스턴스의 내부 값을 수정 할 수 없는 클래스이다. 생성된 시점의 상태를 파괴될때까지 가져간다. 불변 클래스의 규칙 1. 객채의 상태를 변경하는 메서드(변경자)를 제공하지 않는다. 2. 클래스를 확장 할 수 없도록 한다. - 하위 클래스에서 부주의하게 혹은 나쁜 의도로 객체의 상태를 만드는 사태를 막아준다. 3. 모든 필드를 final로 선언한다. 4. 모든 필드를 private으로 선언한다. - 외부에서 직접 접근하여 필드를 수정하는 것을 막아준다. public final로 사용한다면 다음 릴리즈때 내부 표현을 바꾸지.. 2022. 6. 12.
클래스와 인터페이스 클래스와 멤버의 접근 권한을 최소화하라 잘 설계된 컴포넌트는 클래스 내부 데이터와 구현 정보를 외부 컴포넌트에 얼마나 잘 숨겼느냐다. 정보를 은닉해서 구현부와 API를 분리시켜 오직 API로만 외부 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다. 정보 은닉의 장점 1. 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발 할 수 있기 때문이다. 2. 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅 할 수 있고, 다른 컴포넌트로 교체하는 부담도 적기 때문이다. 3. 정보 은닉가 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움을 준다. 완성된 시스템을 프로파일링해 최적화할 컴포넌트를 정한 다음, 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 할 수 .. 2022. 6. 12.
모든 객체의 공통 메서드 Comparable을 구현할지 고려하라 Comparable interface를 통해 인스턴스들에 작연적인 순서를 만들 수 있다. compareTo는 단순 동치성과 순서를 비교 할 수 있다. 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable interface를 구현하자. public interface Comparable { public int compareTo(T o); } compareTo 메서드의 일반 규약 이 객체와 주어진 객체의 순서를 비교한다. · 나보다 작은 객체 음의 정수 · 나보다 같은 객체 0 · 나보다 큰 객체 양의 정수 · 비교 할 수 없는 경우 ClassCastException 규약 1. sgn(x.compareTo(y)) == -sgn(y.x.. 2022. 6. 11.
모든 객체의 공통 메서드 equals를 재정의하려거든 hashCode도 재정의하라. equals를 재정의한 객체의 hashCode재정의 하지 않으면 HashMap, HashSet같은 컬렉션의 원소로 사용 할 때 문제를 일으킨다. hash 일반 규약 1. equals비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. (단, 애플리케이션을 다시 실행한다면 이 값이 달라져도 상관없다.) 2. equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다. 3. equals(Object)가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode가 서로 다른 값을 반환할.. 2022. 6. 6.
모든 객체의 공통 메서드 INTRO Object는 객체를 만들 수 있는 구체 클래스지만 기본적으로는 상속해서 사용하도록 설계되었다. Object에서 final이 아닌 메서드(equals, hashCode, toString, clone, finalize)는 모두 재정의를 염두에 두고 설계된 것이라 재정의 시 지켜야 하는 일반 규약이 명확히 정의되어 있다.일반 규약에 맞게 재정의 하지 않으면 규약을 준수한다고 가정하는 클래스(HashMap, HashSet 등)를 오작동하게 만들 수 있다. equals는 일반 규약을 지켜 재정의하라. equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있어서 자칫하면 끔찍한 결과를 초래한다. 재정의 하지 않으면 클래스의 인스턴스는 오직 자기 자신과만 같게 된다. 재정의 하지 않는 상.. 2022. 6. 6.
객체 생성과 파괴 finalizer와 cleaner 사용을 피하라. finalizer는 예측 할 수 없고, 상황에 따라 위험 할 수 있어 일반적으로는 불필요하다. cleaner는 finalizer보다 덜 위험하지만, 여전히 예측하기 어렵고 느리고 일반적으로는 불필요하다. 피해야 하는 이유 1. 언제 실행될지 알 수가 없어서 제때 실행되어야 하는 작업은 절대 할 수 없다. 2. 상태를 영구적으로 수정하는 작업에서는 절대 사용하면 안된다. DB lock해제를 finalizer나 cleaner에게 맡겨놓는다면 분산 시스템 전체가 서서히 멈출 것이다. 3. 심각한 성능 문제도 동반한다. 4. finalizer공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다. 생성자나 직렬화 과정에서 예외가 발생하면, 이 생성되다 만 객체에서.. 2022. 6. 5.
객체 생성과 파괴 불팔요한 객체 생성을 피하라 똑같은 기능의 객체를 매번 생성하기 보다는 하나를 사용해서 재 사용 하는 것이 성능면에서나 메모리 관리면에서 더 좋다. String s = new String("bikini"); // 잘못된 사용 String s = "bikini"; // 옳은 사용 위에 코드처럼 작성하게 되면 반복문일 경우 객체가 계속해서 생성된다. 책에서도 Boolean(String)을 쓰지말고, Boolean.valueOf(String)을 사용하라고 나와 있다. @HotSpotIntrinsicCandidate public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } java코드를 보면 static으로 하나의 메서드만 생성해서 사용하도.. 2022. 6. 1.
객체 생성과 파괴 생성자에 매개변수가 많다면 빌더를 고려하라. 책에서는 빌더 패턴 전 시기에 2가지를 설명하고 있지만 결론은 builder를 사용하라는 것이기 때문에 builder에 대해서만 정리하였다. 빌더 패턴을 쓰면 장점 1. 빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다. return 타입을 지정해서 child class에서 타입을 강제 반환 시킬 수 있다. 2. 가독성이 좋으며 build()에서 유효성 검사를 할 수 있다. private 생성자나 열거 타입으로 싱글턴임을 보증하라. public static 방식 public class Elvis() { public static final Elvis INSTANCE = new Elvis(); // 생성하지 못하도록 막는다. private Elvis() {.. 2022. 5. 22.