본문 바로가기

STUDY/이펙티브자바

(44)
5-4) 이왕이면 제네릭 타입으로 만들라 Object 기반 Stack public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { this.ensureCapacity(); this.elements[this.size++] = e; } public Object pop() { if(this.size == 0) { throw new EmptyStackException(); } Object result = thi..
5-3) 배열보다는 리스트를 사용하라 배열과 제네릭의 차이 // 컴파일 에러가 발생하지 않는다. Object[] objectArray = new String[1]; // 컴파일 에러가 발생한다. List objectList = new ArrayList() 배열에서는 Sub가 Super의 하위 타입이라면 Sub[]는 Super[]의 하위타입이 된다. 리스트에서는 List가 List의 하위타입이 아니다. 따라서 위의 코드를 보면 배열에서는 가능한 코드가 리스트에서는 컴파일 에러가 발생한다. 배열이 더 유연한 코드를 제공하는거 같지만 실제로는 위험한 코드를 허용해주는 것이다. 배열의 저러한 특성때문에 아래의 코드같이 런타임에 문제가 생길 수 있다. // 런타임에서 발생 Object[] objectArray = new String[1]; objec..
5-2) 비검사 경고를 제거하라 비검사 경고 //비검사 형변환 경고 발생 Set nameSet = new HashSet(); //타입 매개변수를 명시하여 비검사 형변환 경고 제거 Set nameSet = new HashSet(); 제네릭을 사용하기 시작하면 많은 컴파일러 경고를 보게 될 것이다. 위의 코드에서는 보면 Set타입의 변수에 HashSet 객체를 넣으려고 하자 비검사 형변환 경고가 발생했다. HashSet에 타입 파라미터가 없으므로 어떤 타입이 들어갈 지 알 수 없으므로, 런타임에 형변환 문제가 발생 할 수 있다는걸 경고한 것이다. 이는 실제로 위험한 코드이기 때문에 타입 파라미터를 명시하여 비검사 형변환 경고를 제거해주어야 한다. @SuppressWarnings @SuppressWarnings("unchecked") pub..
5-1) 제네릭 클래스의 raw type은 사용하지 마라 제네릭 클래스 public class Printer { private T source; public Printer(T source) { this.source = source; } public void setSource(T source) { this.source = source; } public void print() { System.out.println(source); } } // 제네릭 클래스 덕분에 String, Integer 타입의 클래스를 따로 만들지 않고 사용할 수 있다. Printer strPrinter = new Printer("hello"); strPrinter.print(); Printer intPrinter = new Printer(100); intPrinter.print(); 제네릭 ..
4-10) 멤버 클래스는 되도록 static으로 만들어라 중첩 클래스 중첩 클래스란 다른 클래스 안에 정의된 클래스로서 이너 클래스(inner class)라고도 불린다. 중첩 클래스는 자신을 감싼 외부 클래스에서만 쓰여야 하며, 그 이외에 쓰임새가 있다면 탑레벨 클래스로 만들어야 한다. 중첩클래스는 1) 정적 멤버 클래스 2) 멤버 클래스 3) 익명 클래스 4) 지역 클래스와 같이 4개의 유형이 있다. 정적 멤버 클래스 public class MyClass { private Integer myNumber; public static class MyInnerClass { public void method(MyClass myClass) { Integer number = myClass.myNumber; } } } // 일반 클래스와 같이 사용할 수 있다. MyInne..
4-9) 태그 달린 클래스보다는 클래스 계층구조를 활용하라 태그달린 클래스 class Figure { enum Shape { RECTANGLE, CIRCLE }; // 도형의 형태를 나타내는 태그 필드 final Shape shape; double length; double width; double radius; Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } Figure(double length, double width) { shape = Shape.RECTANGLE; this.length = length; this.width = width; } double area() { switch(shape) { case RECTANGLE: return length * width; case CI..
4-8) 인터페이스는 타입을 정의하는 용도로만 사용하라 인터페이스 MyInf instance1 = new MyInfImpl(); MyInf instance2 = new YourInfImpl(); 자바의 인터페이스는 (일반적으로) 구현이나 필드 없이 메소드만 정의되어있는 추상화된 요소이다. 개발자는 인터페이스를 구현하여 동일한 기능을 내부 구현을 다르게하여 만들 수 있다. 클라이언트에서는 다르게 만들어진 인터페이스의 구현체들을 내부 모습을 모른체로 사용할 수 있다. 이처럼 인터페이스는 다양한 구현체의 인스턴스를 참조할 수 있는 타입 역할을 한다. 상수 인터페이스 안티패턴 // 수학 상수들을 정의한 인터페이스 public interface Math { static final double pi = 3.141592; ... } 상수 인터페이스란 메소드 없이 상수값들..
4-7) 인터페이스는 구현하는 쪽을 생각해 설계하라 인터페이스와 디폴트 메소드 // MyInterface.java // MyInterface의 구현체인 MyClass가 존재하기 때문에 MyInterfece에 메소드를 추가하면 컴파일 에러가 발생한다. public interface MyInterface { public void method1(); } // MyClass.java public class MyClass implements MyInterface { @Override public void method1() { } } 인터페이스의 구현체가 한번 정의되고 나면 인터페이스에 메소드를 새로 추가하기 어렵다. 구현체 클래스는 개발당시의 인터페이스 메소드들을 구현했기 때문에, 새로 추가된 메소드는 미구현 상태가 되고 컴파일 에러가 발생한다. 따라서 인터페이스..