Book
[Item 28] 배열보다는 리스트를 사용하라
배열과 제네릭 타입의 차이 1. 공변과 불공변 배열은 공변이다 sub가 super의 하위 타입이라면 sub[]는 배열 super[]의 하위 타입이 된다. Object[] objectArray = new Long[1]; objectArray[0] = "어떤 타입이든 저장할 수 있는데.........."; // ArrayStoreException 던짐 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다. 그래서 컴파일에는 아무런 영향을 끼치지 않지만 런타임 시 ArrayStoreException을 던진다. 제네릭은 불공변이다. 서로 다른 타입 type1과 type2가 있을 때 List 은 List 의 상위 타입도 아니고 하위 타입도 아니다. // 컴파일 시 예외 List list = new ..
[Item 27] 비검사 경고를 제거하라
제네릭을 사용하기 시작하면 수많은 컴파일러 경고들을 마주치게 되는데 이러한 경고들은 가능한 많이 제거하는 것이 좋다. 경고들을 모두 제거 한다면, 그 코드는 타입의 안정성이 보장된다. 비검사 경고는 쉽게 제거할 수 있다. List list = new ArrayList(); 컴파일 경고가 나오는 코드다. List list = new ArrayList(); 제네릭 타입으로 인스턴스를 생성하고 뒤에 다이아몬드 연산자를 생성해야 한다. 경고를 제거할 수 없지만 타입이 안전하다고 보장되면 경고를 숨겨라 @SuppressWarnings("unchecked")으로 경고를 숨기자. 단, 타입 안정성을 검증하지 않고 경고를 숨기면 절대 안된다. 해당 코드는 경고 없이 컴파일되지만, 런타임 시 여전히 ClassCastEx..
[Item 26] 로 타입(Raw Type)은 사용하지 말라
로 타입은 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다. List list = new ArrayList(); 위와 같은 형태를 말한다. 로 타입의 문제점 타입 안정성과 표현력을 확보할 수 없다. 문제점이 컴파일 시에 걸러내지 못하고 런타임에 예외가 발생한다. 따라서 매개 타입을 선언해서 타입 안정성과 표현력을 가져가야 한다. List와 List의 차이점 임의의 객체 타입을 받는다는 표현을 한 것이고 두 동작은 같을 것이다. 대신에 컴파일러가 이 사실을 아냐 모르냐에 차이점이 있다. Object 의 매개 타입은 모든 타입을 허용한다는 표현을 한것이고 컴파일러에게 전달한다. 이로 인해 List은 하위 타입 규칙과 타입 안정성을 모두 지킬 수 있게 된다. 매개 변수 타입에서 사용된다면 pu..
[Item 25] 톱레벨 클래스는 한 파일에 하나만 담으라
소스를 작성하는 파일 내에 하나만 존재하는 클래스를 톱레벨 클래스라고 한다. 소스 파일 하난에 여러개의 톱레벨 클래스가 있다면 문제가 있다. 문제 Product.java class Product { static final StringNAME= "phone"; } class Company { static final StringNAME= "samsung"; } Company.java public class Company { static final StringNAME= "samsung"; } class Product { static final StringNAME= "phone"; } 위의 소스코드는 컴파일 에러에서 중복된 클래스라 에러가 발생할 것이다. 컴파일러에게 어느 소스파일을 먼저 읽게 하냐 에 따라 정..
[Item 24] 멤버 클래스는 되도록 static 으로 만들라
중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다. 자신을 감싼 바깥 클래스에서만 쓰여야 한다. 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 중첩 클래스의 종류 정적 멤버 클래스 (비정적) 멤버 클래스 익명 클래스 지역 클래스 정적 멤버 클래스를 제외한 나머지 3가지가 내부 클래스(inner class)에 해당한다. 정적 멤버 클래스와 비정적 멤버 클래스는 구문상 차이는 단지 static이 붙어 있고의 차이뿐이지만, 의미상 차이는 꽤 크다. 비정적 멤버 클래스 인스턴스 생성 public class Outer { private int a; class inner { private int b; } } Outer outer = new Outer(); Outer.inne..
[Item 23] 태그 달린 클래스보다 클래스 계층구조를 활용하라
태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다. 클래스 계층구조보다 나쁘다. public class Figure { enum Shape {RECTANGLE,CIRCLE} private final Shape shape; private int length; private int width; private int radius; public Figure(int radius) { this.shape = Shape.CIRCLE; this.radius = radius; } public Figure(int length, int width) { this.shape = Shape.RECTANGLE; this.length = length; this.width = width; } public int area(..
[Item 22] 인터페이스는 타입을 정의하는 용도로만 사용하라
인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 달리 말해, 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는 것이다. 인터페이스는 오직 이 용도로만 사용해야 한다. 상수 인터페이스 안티패턴 public interface PhysicalConstants { static final intFIRST= 1; static final intSECOND= 2; static final intTHREE= 3; } 이러한 인터페이스는 안티 패턴이므로 사용하지 말자. 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당한다. 클라이언트 코드가 내부 구현에 해당하는 이 상수들에 종속되게 한다. 상수를 공개하는 방..
[Item 21] 인터페이스는 구현하는 쪽을 생각해 설계해라
자바 8 전에는 기존 구현체를 깨뜨리지 않고는 인터페이스에 메서드를 추가할 방법이 없었다. 인터페이스에 메서드를 추가하면 기존 구현체에 이미 메서드가 존재하는 가능성이 적기에 자바 8 이후에 기존에 인터페이스에 메서드를 추가할 수 있도록 디폴트 메서드를 제공했다. 디폴트 메서드는 주로 람다를 활용하기 위해 선언하는데, 디폴트 메서드를 선언하면, 그 인터페이스를 구현한 후 디폴트 메서드를 정의 하지 않은 모든 클래스에서 사용이 가능하다. 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어려운 법이다. Collection 인터페이스의 removeIf() 의 디폴트 메서드를 보면 모든 Collection 구현체와 어우러지는 것은 아니다. Collection 인터페이스를 구현한 SynchronizedCollec..
[Item 20] 추상 클래스보다는 인터페이스를 우선하라
자바에서 제공하는 다주우 구현 메커니즘 인터페이스 추상 클래스 자바 8 이후에 인터페이스도 디폴트 메서드를 제공할 수 있게 되어 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있다. 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다. 상속을 통해 하위 클래스가 되어야 된다라는 말인거 같다. 자바는 단일 상속만 지원 추상 클래스 방식은 새로운 타입을 정의하는 데 제약을 안게 되는 셈이다. 하위 클래스가 되어야 하는 제약 인터페이스가 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급된다. 차이점 정리 추상 클래스를 구현한 클래스는 하위 클래스가 되는 제약을 갖는다. 인터페이스는 ..
![[Item 19] 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaPgke%2FbtrH32uQ0iS%2Fny6XJLxsAG6DqUH3pQ3F61%2Fimg.png)
[Item 19] 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다. 앞서 item 18에서 HashSet 클래스를 상속하여 add를 재정의 한 것이 addAll 까지 영향을 준다는 사실을 알 수 없었다. 이러한 문제점을 문서화하여 주석처리만 해줘도 add 메서드로 내부 구현이 되있구나 라는 것을 알 수 있다. 좋은 문서화는 무엇일까? 문서화를 좋게 작성 하려면 어떻게가 아닌 무엇을 하는지를 설명해야한다. 즉, 내부 구현 방식을 설명해야 한다. 효율적인 하위 클래스를 만들려면? 클래스 내부 동작 과정 중간에 끼어들 수 있는 훅(hook)을 잘 선변하여 protected 메서드 형태로 공개해야 할 수 도 있다. java.util.AbstractList 의 removeRange 메서..
[Item 18] 상속보다는 컴포지션을 사용하라
코드를 재사용하는데 상속은 강력한 수단이다. 하지만 항상 재사용하는데에 있어 좋은 것은 아니다. 같은 패키지 내에서 상위 클래스와 하위 클래스를 모두 통제한다면 좋지만 다른 패키지 내에서 상속을 한다면 관리가 쉽지 않을 것이다. 메서드 호출과 달리 상속은 캡슐화를 깨뜨린다. 상위 클래스를 확장을 고려하지 않고 설계하면 하위 클래스에서 에러가 날 수 있기에 충분히 고려해서 설계하자. HashSet을 잘못 상속한 코드 예 @Getter @NoArgsConstructor @AllArgsConstructor public class InstrumentedHashSet extends HashSet { private int addCount = 0; @Override public boolean add(E e) { ad..
[Item 17] 변경 가능성을 최소화 하라
불변 클래스란 그 인스턴스의 내부 값을 수정할 수 없는 클래스다. 불변 인스턴스가 가지고 있는 정보는 고정되어 객체가 파괴되는 순간까지 달라지지 않는다. 자바 라이브러리에도 다양한 불변 클래스가 있다. String 기본 타입의 박싱된 클래스 BigInteger BigDecimal 가변 클래스보다 설계와 구현이 쉬우며 오류가 생길 여지도 적고 안전하다. 불변 클래스를 만드는 다섯가지 규칙 객체의 상태를 변경하는 메서드를 제공하지 않는다 setter() 메서드를 예를 들 수 있겠다. 클래스를 확장할 수 없도록 한다. final 키워드를 사용한다. 모든 필드를 final로 선언한다. 새로 생성된 인스턴스를 동기화 없이 다른 스레드로 건네도 문제없이 동작하게끔 보장하는 데 필요 모든 필드를 private 으로 ..
[Item 16] public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
객체 지향 프로그램은 public 클래스에 public 필드를 사용하는 것은 위험한 일이다. private 으로 변경하고 이를 제공 할 접근자를 추가한다. 접근자는 getter() 이다. 아래 코드는 lombok으로 대체 했다. @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Point { // public 필드 말고 private 필드를 사용하고 접근자를 추가하여 사용하자 private int x; private int y; } 접근자를 제공함으로써 클래스 내부 표현 방식을 언제들 바꿀수 있는 유연성을 제공한다. package-private(default) 클래스 혹은 private 중첩 클래스라면 데이터 필드를 노출할 때가 좋을..
[Item 15] 클래스와 멤버의 접근 권한을 최소화하라
어설프게 설계한 컴포넌트와 잘 설계된 컴포넌트의 차이점은 내부 데이터를 얼마나 잘 숨겼냐에 따른다. 캡슐화 혹은 정보 은닉이 설계의 기반이되는 원리이다. 캡슐화의 장점 여러 컴포넌트를 병렬로 개발이 가능하므로 시스템 개발 속도를 높인다. 각 컴포넌트를 빠르게 파악할 수 있고 시스템 교체하는 부담도 적기 때문에 시스템 관리 비용을 낮춘다. 캡슐화 자체만으로 성능을 높여주지 않지만 각 컴포넌트에 영향을 주지 않고 해당 컴포넌트만을 최적화 할 수 있기 때문에 성능 최적화에 도움을 준다. 소프트웨어 재사용을 높인다. 개발 난이도를 낮춰준다. 캡슐화의 접근성은 접근제어자로 정해진다. 기본원칙 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다. 패키지 외부에서 쓸 이유가 없으면 package-private으로 ..
[Item 14] Comparable을 구현할지 고려하라
CompareTo는 동치성 비교에 순서까지 비교할 수 있으며 제네릭 하다. Comparable을 구현했다는 것은 해당 클래스의 순서가 있음을 뜻한다. Arrays의 클래스는 Comparable을 구현하여 아래와 같이 손쉽게 정렬할 수 있다. Arrays.sort(arr); 검색, 극단값 계산, 자동 정렬되는 컬렉션 관리도 쉽게 할 수 있다. 자바의 라이브러리의 모든 값 클래스와 열거타입이 Comparable을 구현했다. 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable을 구현하자. comparTo 메서드 규약 이 객체와 주어진 객체의 순서를 비교한다. 이 객체가 주어진 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환한다. 객체와 비교할 수 없는 타..