Don't give up!

[JAVA] 자바의 정석 정리 (12장) 본문

개발서적/JAVA의 정석

[JAVA] 자바의 정석 정리 (12장)

Heang Lee 2021. 6. 7. 02:49
자바의 정석을 읽고 정리한 내용입니다.

Java의 정석 - YES24

 

Java의 정석

최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 `자바의 정석`의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하고 쓴 책. 뿐만 아

www.yes24.com


1. 지네릭스(Generics)

(1) 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능. 객체의 타입을 컴파일 시 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

(2) 지네릭스는 타입 안정성을 제공하고, 타입체크와 형변환을 생략할 수 있어 코드가 간결해진다는 장점이 있다.

(3) 지네릭 타입은 메서드와 클래스에 선언할 수 있다. 타입 변수를 '<T>'와 같이 사용할 수 있으며 T가 아닌 다른 것을 사용해도 된다. 상황에 맞게 의미있는 문자를 선택해서 사용하는 것이 좋다.

(4) 지네릭 타입에 'extends'를 사용하면 특정 타입의 자손들만 대입할 수 있게 제한할 수 있다. interface에도 extends를 사용하여 구현한 클래스를 지네릭 타입으로 설정할 수 있다. 클래스와 인터페이스 모두 제한을 두고자 한다면 & 기호로 연결하여 제한할 수 있다.

(5) 지네릭 클래스가 아니거나 static메서드에는 타입 매개변수를 사용할 수 없다. 지네릭스를 적용하지 않던가, 매개변수 대신 특정 타입을 지정해주어야 한다.

(6) 지네릭 타입이 다른 것 만으로는 오버로딩이 성립하지 않는다. 지네릭 타입은 컴파일러가 컴파일할 때만 사용하고 제거해버리기 때문. 이때 와일드 카드 기호 '?'로 표현하여 어떠한 타입도 될 수 있음을 명시한다.

(7) 와일드 카드 기호 ?는 Object 타입과 다를게 없지만 'extends T'(T와 그 자손들만 가능) 또는 'super T'(T와 그 조상들만 가능)로 상한과 하한을 제한할 수 있다. 다만 매개변수를 특정 클래스의 객체로 사용하고자 할 때 그 클래스의 자손으로서 제한되어 있어야 한다.

(8) 메서드의 선언부에 지네릭 타입이 선언된 메서드를 지네릭 메서드라 한다. 지네릭 타입의 선언 위치는 반환 타입 바로 앞이다.

(9) 지네릭 클래스에 정의된 타입 매개변수와 지네릭 메서드에 정의된 타입 매개변수는 전혀 별개의 것이다. 같은 문자를 사용해도 같은 것이 아니라는 점에 주의해야 한다.

(10) 지네릭 타입과 넌지네릭 타입간의 형변환은 항상 가능하다. 하지만 다른 지네릭 타입 간 형변환은 불가능하다. 와일드 카드가 포함된 지네릭타입으로 형변환을 거쳐야만 한다.

(11) 컴파일러는 지네릭 타입을 이용해서 소스파일을 체크하고, 필요한 곳에 형변환을 넣어준 후 지네릭 타입을 제거한다. 컴파일된 파일(*.class)에는 지네릭 타입에 대한 정보가 없다. 지네릭이 도입되기 이전의 소스 코드와 호환성을 유지하기 위함. 가능하면 원시 타입을 사용하지 않도록 해야 한다.

2. 열거형(enums)

(1) 열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.

(2) 자바의 열거형은 C언어의 열거형보다 더 향상된 것으로 열거형의 값 뿐만 아니라 타입도 관리하기 때문에 논리적인 오류를 줄일 수 있다.

(3) 자바의 열거형은 타입에 안전한 열거형(typesafe enum)이라서 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다. 

(4) 상수의 값이 바뀌면 상수를 참조하는 모든 소스를 다시 컴파일해야한다. 하지만 열거형 상수를 사용하면 기존의 소스를 다시 컴파일하지 않아도 된다.

(5) java.lang.Enum클래스는 모든 열거형의 조상이다. Enum 클래스에서 values( )함수를 호출하면 열거형에 정의된 모든 상수를 배열에 담아 반환한다.

메서드 설명
T[] values( ) 열거형의 모든 상수를 배열에 담아 반환한다.
Class<E> getDeclaringClass( ) 열거형의 Class 객체를 반환한다.
String name( ) 열거형 상수의 이름을 문자열로 반환한다.
int ordinal( ) 열거형 상수가 정의된 순서를 반환한다.(0부터 시작)
T valueOf(Class<T> enumType, String name) 지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다.

(6) 열거형 상수의 값이 불연속적인 경우에는 열거형 상수 이름 옆에 원하는 값을 괄호( )와 함께 적어 선언할 수 있다.

enum Direction {
    EAST(1),
    SOUTH(5),
    WEST(-1),
    NORTH(10);
}

(7) 열거형의 생성자는 제어자가 묵시적으로 private이다. 열거형의 객체를 생성할 수 없다.

(8) 하나의 열거형 상수에 여러 값을 지정할 수도 있다. 다만 그에 맞게 인스턴스 변수와 생성자 등을 새로 추가해주어야 한다.

enum Direction {
    EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4, "^");
    private final int value;
    private final String symbol;
    Direction(int value, String symbol) {
    	this.value = value;
        this.symbol = symbol;
    }
    public int getValue() { return value; }
    public String getSymbol() { return symbol; }
}

(9) 열거형에 추상 메서드를 선언할 수 있다. 이때 각 열거형 상수가 이 추상 메서드를 반드시 구현해야 한다.

enum Transportation {
    BUS(100) { 
    	int fare(int distance) { return distance*BASIC_FARE; } 
    },
    TRAIN(150) {
    	int fare(int distance) { return distance*BASIC_FARE; } 
    };
    
    abstract int fare(int distance);
    protected final int BASIC_FARE;	//protected로 해야 각 상수에서 접근가능
    
    Transportation(int basicFare) {
    	BASIC_FARE = basicFare;
    }
    public int getBasicFare() { return BASIC_FARE; }
}

(10) 열거형 상수는 static final 클래스 객체이다. 객체의 주소이고 값은 바뀌지 않는 값이므로 '=='로 비교가 가능하다.

3. 애너테이션(annotation)

(1) 애너테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있다.

(2) 애너테이션은 알리는 역할을 할 뿐, 메서드가 포함된 프로그램 자체에는 아무런 영향을 미치지 않는다.

(3) 메타 애너테이션은 애너테이션을 정의하는데 사용되는 애너테이션의 애너테이션이다.

애너테이션 설명
@Override 컴파일러에게 오버라이딩하는 메서드라는 것을 알린다.
@Deprecated 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다.
@SuppressWarnings 컴파일러의 특정 경고메시지가 나타나지 않게 해준다.
@SafeVarargs 지네릭스 타입의 가변인자에 사용한다.
@FunctionalInterface 함수형 인터페이스라는 것을 알린다.
@Native native 매서드에서 참조되는 상수 앞에 붙인다.
@Target 애너테이션이 적용가능한 대상을 지정하는데 사용한다.
@Documented 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
@Inherited 애너테이션이 자손 클래스에 상속되도록 한다.
@Retention 애너테이션이 유지되는 범위를 지정하는데 사용한다.
@Repeatable 애너테이션을 반복해서 적용할 수 있게 한다.

(4) @Override를 붙이면 컴파일러가 같은 이름의 메서드가 조상에 있는지 확인하고 없으면 에러메시지를 출력한다.

(5) @Deprecated가 붙은 대상은 주의 메시지를 출력할 뿐 컴파일을 중지하지 않는다. 애너테이션이 붙은 대상을 사용하지 않도록 권할 뿐 강제성은 없다.

(6) @FunctionalInterface는 함수형 인터페이스를 올바르게 선언했는지 확인하고, 잘못된 경우 에러를 발생시킨다. 실수를 방지할 수 있으므로 함수형 인터페이스를 선언할 때 해당 애너테이션을 붙이는 것이 좋다.

(7) @SuppressWarnings는 컴파일러가 보여주는 경고 메시지가 나타나지 않게 억제해준다. 묵인해야하는 경고가 발생하는 대상에 반드시 붙여서 컴파일 후에 어떤 경고 메시지도 나타나지 않게 해야한다. 자주 억제되는 경고 메시지의 종류는 "deprecation", "unchecked"(지네릭스로 타입을 지정하지 않았을 때 발생하는 경고), "rawtypes"(지네릭스를 사용하지 않아서 발생하는 경고), "varargs"(가변인자의 타입이 지네릭 타입일 때 발생하는 경고)이다.

둘 이상의 경고를 동시에 억제하고자 할 때는 배열처럼 { }를 추가로 사용해야 한다.

(8) 메서드를 선언할 때 @SafeVarargs를 붙이면 메서드를 호출하는 곳에서 발생하는 경고도 억제된다. 반면 @SuppressWarnings("unchecked")로 경고를 억제하려면 메서드 선언뿐만 아니라 메서드가 호출되는 곳에도 애너테이션을 붙여야한다. @SafeVarargs로 'unchecked'경고는 억제할 수 있지만 'varargs'경고는 억제할 수 없다. 가능하면@SuppressWarnings("varargs")를 같이 붙이는 것이 좋다.

(9) @interface로 애너테이션을 정의할 수 있다. @를 붙이는 것을 제외하면 인터페이스로 정의하는 것과 동일하다. 메타 애너테이션을 붙여 애너테이션을 설정할 수 있다.

(10) 애너테이션에도 인터페이스처럼 상수를 정의할 수 있지만 디폴트 메서드는 정의할 수 없다.

(11) 애너테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며 상속을 통해 구현하지 않아도 된다. 다만 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해주어야 한다.

요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용된다. 또한 매개변수, 예외를 선언할 수 없으며 타입 매개변수로 요소를 정의할 수 없다.

@interface TestInfo {
    int count();
    String testedBy();
}

@TestInfo(count=3, testedBy="KIM")
public class A { ... }

(12) 모든 애너테이션의 조상은 Annotation이다. 그러나 애너테이션은 상속이 허용되지 않으므로 Annotation을 조상으로 지정할 수 없다.