자바의 정석 ch12. 지네릭스, 열거형, 애너테이션
1. 지네릭스(Generics)
1.1 지네릭스란?
- 지네릭스: 다양한 타입의 객체를 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입체크를 해주는 기능
- 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어듦
지네릭스 장점
- 타입 안정성 제공
- 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해짐
1.2 지네릭 클래스의 선언
class Box {
object item;
void setItem(Object item) {this.item = item;}
Object getItem() {return Item;}
}
//지네릭 클래스로 변경→클래스 옆에 '<T>'를 붙이면 됨+Object를 T로 바꾸기
class Box<T> {
T item;
void setItem(T item) {this.item = item;}
T getItem() {return item;}
}
- T, E, K, V...: 타입변수(임의의 참조형 타입)
Box<String> b = new Box<String>(); //타입 T대신 실제 타입(String) 지정
b.setItem(new Object()); //에러. String이외의 타입 지정불가
b.setItem("ABC"); //String타입이므로 가능
String item = b.getItem();
지네릭스의 제한
- 지네릭 클래스 Box의 객체를 생성할 때, 객체별로 다른 타입을 지정하는 것은 적절하다. 지네릭스는 이처럼 인스턴스 별로 다르게 동작하도록 하려고 만든 기능임.
Box<Apple> appleBox = new Box<Apple>(); //Apple객체만 저장가능
Box<Grape> grapeBox = new Box<grape>(); //Grape객체만 저장가능
- 그러나 모든 객체에 대해 동일하게 동작해야하는 static멤버에 타입 변수 T를 사용할 수는 없음. T는 인스턴스변수로 간주되기 때문.
- 지네릭 타입의 배열을 생성하는 것도 허용되지 않음. 배열 타입의 참조변수를 선언하는 것(ex. T[] itemArr)은 가능 but 'new T[10]'과 같이 배열을 생성하는 것은 안됨.
1.3 지네릭 클래스의 객체 생성과 사용
대입된 타입이 다르면 에러. 상속관계도 해당. 단, 두 지네릭 클래스의 타입이 상속관계에 있고, 대입된 타입이 같은 것은 괜찮음.
그리고 추정이 가능한 경우에는 타입을 생략할 수 있게됨.
Box<Apple> appleBox = new Box(); //가능!
1.4 제한된 지네릭 클래스
- 타입 문자로 사용할 문자를 명시하면 한 종류의 타입만 저장할 수 있도록 제한할 수 있지만, 그래도 여전히 모든 종류의 타입을 지정할 수 있다는 것에는 변함이 없다.
- 그렇다면 타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한할 수 있는 방법은 없을까?
A. 지네릭 타입에 extends를 사용하면 특정 타입의 자손들만 대입할 수 있다.
class FruitBox<T extends Fruit> { //fruit자손만 타입으로 지정가능
ArrayList<T> list = new ArrayList<T>();
1.5 와일드 카드
- 지네릭 타입이 다른 것만으로는 오버로딩이 성립되지 않음. 메서드 중복 정의로 적용됨.
- 이를 해결하기 위해 고안된 것이 '와일드 카드'
<? extends T> //와일드 카드의 상한 제한, T와 그 자손들만 가능
<? super T> //와일드 카드의 하한 제한, T와 그 조상들만 가능
<?> //제한 없음, 모든 타입이 가능. <? extends Object>와 동일
1.6 지네릭 메서드
- 메서드의 선언부에 지네릭 타입이 선언된 메서드를 지네릭 메서드라 함.
static <T> void sort(List<T> list, Comparator<? super T> c)
- 지네릭 클래스에 정의된 타입 매개변수
class FruitBox<T>
와 지네릭 메서드에 정의된 타입 매개변수
static <T> void sort(List<T> list, Comparator<? super T> c)
는 완전 별개의 것. 같은 타입의 문자 T를 사용한다 해도 같은 것이 아님.
- 지네릭 메서드를 호출할 때, 대입된 타입을 생략할 수 없는 경우에는 참조변수나 클래스 이름을 생략할 수 없다.
1.7 지네릭 타입의 형변환
- 지네릭 타입과 넌지네릭 타입간의 형변환은 항상 가능. 다만 경고 발생.
- 대입된 타입이 다른 지네릭 타입 간에는 형변환이 불가능함.
1.8 지네릭 타입의 제거
- 컴파일러는 지네릭 타입을 이용해서 소스파일을 체크하고, 필요한 곳에 형변환을 넣어준다. 그리고 지네릭 타입을 제거한다. 즉, 컴파일된 파일(*.class)에는 지네릭 타입에 대한 정보가 없는 것이다. 이렇게 하는 주된 이유는 지네릭이 도입되기 이전의 소스 코드와의 호환성을 유지하기 위해서이다.
- 기본적인 제거과정
- 지네릭 타입의 경계를 제거한다.
- 지네릭 타입을 제거한 후에 타입이 일치하지 않으면, 형변환을 추가한다.
2. 열거형(enums)
2.1 열거형이란?
- 자바의 '타입에 안전한 열거형'에서는 실제 값이 같아도 타입이 다르면 조건식의 결과가 false가 된다. 이처럼 값뿐만 아니라 타입까지 체크하기 때문에 타입에 안전하다.
2.2 열거형의 정의와 사용
enum 열거형이름 { 상수명1, 상수명2, ...}
//ex. 동서남북 4방향을 상수로 정의하는 열거형 Direction
enum Direction { EAST, SOUTH, WEST, NORTH }
2.3 열거형에 멤버 추가하기
- 열거형 상수의 값이 불규칙적인 경우에는 다음과 같이 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 된다.
enum Direction {EAST(1), SOUTH(5), WEST(-1), NORTH(10);}
- 열거형에 추상 메서드를 선언하면 각 열거형 상수가 이 추상 메서드를 반드시 구현해야 함.
2.4 열거형의 이해
enum Direction { EAST, SOUTH, WEST, NORTH }
- 열거형 상수 하나하나가 Direction객체이다.
3. 애너테이션(annotation)
3.1 애너테이션이란?
- 소스코드에 대한 문서를 따로 만들기보다 소스코드와 문서를 하나의 파일로 관리하는 것이 낫다고 생각. 그래서 소스코드의 주석에 소스코드에 대한 정보를 저장하고, 소스코드의 주석으로부터 HTML문서를 생성해내는 프로그램을 만들어 사용했다.
- 애너테이션: 프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것.
- 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있다는 장점
ex.
@Test //이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
...
}
3.2 표준 애너테이션
- 메타 애너테이션: 애너테이션을 정의하는데 사용되는 애너테이션의 애너테이션
@Override
- 메서드 앞에만 붙일 수 있는 애너테이션, 조상의 메서드를 오버라이딩하는 것이라는 걸 컴파일러에게 알려주는 역할
@Deprecated
- 더이상 사용되지 않는 필드나 메서드에 사용
- 이 애너테이션이 붙은 대상은 다른 것으로 대체되었으니 더이상 사용하지 않을 것을 권함
@functionalInterface
- 함수형 인터페이스를 선언할 때, 이 애너테이션을 붙이면 컴파일러가 올바르게 선언했는지 확인하고 잘못된 경우에는 에러 발생
@SuppressWarnings
- 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제
- 주로 사용되는 것은 "deprecation"('@Deprecated'가 붙은 대상을 사용해서 발생하는 경고), "unchecked"(지네릭스 타입을 지정하지 않았을 때 발생하는 경고), "rawtypes"(지네릭스를 사용하지 않아서 발생하는 경고), "varags"(가변인자의 타입이 지네릭 타입일 때 발생하는 경고)를 억제
@SafeVarags
- 메서드에 선언된 가변인자의 타입이 non-reifiable타입일 경우, 해당 메서들르 선언하는 부분과 호출하는 부분에서 "unchecked"경고가 발생한다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 '@SafeVarags'를 사용해야 함.
- 생성자와 static이나 final이 붙은 메서드에만 붙일 수 있음. 즉, 오버라이드 될 수 있는 메서드에는 사용불가
3.3 메타 애너테이션
- 애너테이션에 붙이는 애너테이션으로, 애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간 등을 지정하는데 사용
@Target
- 애너테이션이 적용가능한 대상을 지정하는데 사용
@Retantion
- 애너테이션이 유지되는 기간을 지정하는데 사용
@Documented
- 애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 함
@Inherited
- 애너테이션이 자손 클래스에 상속되도록 함
@Repeatable
- 여러번 붙일 수 있음
ex. @ToDo라는 애너테이션이 정의되어 있을 때 '@ToDo'를 여러 번 붙이는 것이 가능
@repeatable(ToDos.class)
@interface ToDo {
String value();
}
@ToDo("deleted test codes")
@ToDo("override inherited methods")
class MyClass{
...
}
@Native
- 네이티브 메서드에 의해 참조되는 상수필드에 붙이는 애너테이션
- 네이티브 메서드: JVM이 설치된 OS의 메서드
- 모든 클래스의 조상인 Object클래스의 메서드들은 대부분 네이티브 메서드
3.4 애너테이션 타입 정의하기
- 직접 애너테이션을 만들어서 사용
interface 애너테이션 이름 {
타입 요소이름(); //애너테이션의 요소 선언
...
}
- @Override는 애너테이션, Override는 애너테이션의 타입인 것임.
@interface TestInfo {
int count();
String testedBy();
String[] testTools();
TestType testType();
DateType testDate();
}
@TestInfo(
count = 3,
testedBy = "kim",
testTools = {"JUnit", "AutoTester"},
testType = TestType.FIRST,
testDate = @DateTime(yymmdd = "160101", hhmmss = "235959")
)
public class NewClass {...}
- 애너테이션의 각 요소는 기본값을 가질 수 있으며, 기본값이 있는 요소는 애너테이션을 적용할 때 값을 지정하지 않으면 기본값이 사용됨
java.lang.annotation.Annotation
- 모든 애너테이션의 조상은 Annotation
- 애너테이션은 상속이 허용되지 않으므로 아래와 같이 명시적으로 Annotation을 조상으로 지정할 수 없음
@interface TestInfo extends Annotation { //에러
int count();
}
마커 애너테이션
- 값을 지정할 필요가 없는 경우, 애너테이션의 요소를 하나도 정의하지 않을 수 있다. 이처럼 요소가 하나도 정의되지 않은 애너테이션을 마커 애너테이션이라고 함.
@Target (ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{} //마커 애너테이션. 정의된 요소가 없음
애너테이션 요소의 규칙
- 요소의 타입은 기본형, String, enum, 애너테이션, class만 허용
- ()안에 매개변수를 선언할 수 없음
- 예외를 선언할 수 없음
- 요소를 타입 매개변수로 정의할 수 없음
Author And Source
이 문제에 관하여(자바의 정석 ch12. 지네릭스, 열거형, 애너테이션), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yuju9/자바의-정석-ch12.-지네릭스-열거형-애너테이션저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)