(오키) 자바의정석, 챕터 12 - Generics, Enum, Annotation

12-1 Generics 란?

  • 컴파일시 타입을 체크해 주는 기능 (compile-time type check) - JDK1.5
  • 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줌

12-2 타입변수

  • 클래스를 작성 할때, Object 타입 대신 타입 변수(E)를 선언해서 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ArrayList extends AbstractList {
private transient Object[] elementData;
public boolean add(Object o) { }
public Object get(int index) { }
// ...
}

public class ArrayList<E> extends AbstractList<E> {
private transient E[] elementData;
public boolean add(E o) { }
public E get(int index) { }
// ...
}

12-3 타입 변수에 대입하기

  • 객체를 생성시, 타입 변수(E) 대신 실제 타입(Tv)을 지정(대입)
  • 타입 변수 대신 실제 타입이 지정되면, 형변환 생략 가능
1
ArrayList<Tv> tvList = new ArrayList<Tv>();

12-4 Generics 용어

1
2
3
Box<T>    지네릭 클래스 'T의 Box' 또는 'T Box' 라고 읽는다.
T 타입변수 또는 타입 매개 변수 (T는 타입 문자)
Box 원시 타입 (Raw type)

12-7 Iterator<E>

  • 클래스를 작성할 때, Object 타입 대신 T와 같은 타입 변수를 사용

12-8 HashMap <K,V>

  • 여러 개의 타입 변수가 필요한 경우, 콤마(,)를 구분자로 선언
1
2
HashMap<String, Student> map = new HashMap<String, Student>(); // 생성
map.put("자바왕", new Student("자바왕", ..));

12-9 제한 된 지네릭 클래스

  • extends 로 대입할수 있는 타입을 제한
1
2
3
4
class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정 가능
ArrayList<T> list = new ArrayList<T>();
//...
}
  • 인터페이스인 경우에도 extends를 사용
1
2
3
4
interface Eatable {}
class FruitBox<T extends Eatable> {
// ...
}

12-11 지네릭스의 제약

  • 타입 변수에 대입은 인스턴스 별로 다르게 가능
1
2
Box<Apple> appleBox = new Box<Apple>();
Box<Grape> grapeBox = new Box<Grape>();
  • static 멤버에 타입 변수 사용 불가
1
2
3
4
5
6
class Box<T> {
static T item; // 에러
static int compare(T t1, T t2) { // 에러
// ...
}
}
  • 배열 생성 할때 타입 변수 사용 불가. 타입 변수로 배열 선언은 가능
1
2
3
4
5
6
class Box<T> {
T[] itemArr;
T[] toArray() {
T[] tmpArr = new T[itemArr.length]; // 에러
}
}

12-12 와일드 카드 <?>

  • 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
와일드 카드의 상환 제한 T와 그 자손들만 가능 와일드 카드의 하한 제한 T와 그 조상들만 가능
       제한 없음  <? extends Object> 와 동일

12-14 Generic methods, 지네릭 메서드

  • 지네릭 타입이 선언된 메서드(타입 변수는 메서드 내에서만 유효)
1
static <T> void sort(List<T> list, Comparator<? super T> c);
1
2
3
4
5
6
7
8
- 클래스의 타입 매개변수<T>와 메서드의 타입 매개변수 <T>는 별개

class FruitBox<T> {
//...
static <T> void sort(List<T> list, Comparator<? super T> c) {
// ...
}
}
  • 메서드를 호출 할때마다 타입을 대입해야(대부분 생략 가능)

12-15 지네릭 타입의 형변환

  • 지네릭 타입과 원시 타입 간의 형변환은 바람직 하지 않다. (경고 발생)
1
2
3
Box<Object> objBox = null;
Box box = (Box) objBox;
objBox = (Box<Object>) box;

12-16 지네릭 타입의 제거

  • 컴파일러는 지네릭 타입을 제거 하고, 필요한 곳에 형변환을 넣는다.
  1. 지네릭 타입의 경계(bound)를 제거
  2. 지네릭 타입 제거 후에 타입이 불일치하면, 형변환 추가
  3. 와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가

12-17 열거형 (Enum)

  • 관련된 상수들을 같이 묶어 놓은 것
    enum 열거형이름 { 상수명1, 상수명2, 상수명3, ... }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Card {
static final int CLOVER =0;
static final int HEART =1;
static final int DIAMOND =2;

static final int TWO = 0;
static final int THREE =1;

final int kind;
final int num;
}
// 여기서 if (Card.CLOVER == Card.TWO) 하면 true 되지만 의미는 false 되어야 한다.

class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE }
enum Value { TWO, THREE, FOUR }

final Kind kind;
final Value value;
}
// enum 경우 하면 컴파일 에러 발생, 왜 ? 값만 체크가 아닌 타입 또한 체크 한다.
// if(Card.Kind.CLOVER == Card.Value.TWO) // 컴파일 에러가 발생
1
2
3
4
5
6
7
8
class Unit {
int x, y;
Direction dir;

void init() {
dir = Direction.EAST;
}
}
  • 열거형 상수의 비교에 == 와 compareTo() 사용 가능
1
2
3
4
5
6
7
if(dir = DIRECTION.EAST) {
x++;
} else if( dir > Direction.WEST) { // 에러
// ..
} else if( dir.compareTo(Direction.WEST) > 0 ) {
// compareTo() 는 가능
}

12-19 열거형의 조상 - java.lang.Enum

  • 모든 열거형은 Enum의 자손, 아래 메서드를 상속 받는다
1
2
3
String name(); // 열거형 상수의 이름을 문자열로 반환
int ordinal(); // 열거형 상수가 정릐된 순서를 반환 (0부터 시작)
T valueOf(Class<T> enumType, String name); // name과 일치 하는 열거형 상수 반환

values(), valueOf()는 컴파일러가 자동으로 추가

1
2
3
4
5
6
7
8
9
10
11
12
static E[] values();
static E valueOf(String name);

enum Direction { EAST, SOUTH, WEST, NORTH }

Direction[] dArr = Direction.values();
for(Direction d : dArr)
System.out.printf("%s %d", d.name(), d.ordinal() );

Direction d1 = Direction.EAST; // 열거형타입.상수이름
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");

ch12-21,22 열거형에 멤버 추가하기

  • 불연속적인 열거형 상수, 괄호()안에 적을수 있다 이때 선언 이후 끝에 ; 필요 와 생성자가 필요 함
1
2
3
4
5
6
7
8
9
10
enum Direction { 
EAST(1), SOUTH(5), WEST(-1), NORTH(10);

private final int value;
Direction(int value)
{
this.value = value;
}

}

ch12-23 애너테이션이란?

  • 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공

ch12-25 @Override

  • Overide 하는 함수에 적어주면 javac 에서 확인 체크 해줌

ch12-26 @Deprecated

  • 앞으로 사용 하지 않을 것을 권장 하는 필드나 메서드에 붙인다

ch12-27 @FunctionalInterface

  • 함수형 인터페이스에 붙이면, 컴파일로가 올바르게 작성 했는지 체크
  • 함수형 인터페이스에는 하나의 추상 메서드만 가져야 한다는 제약이 있음
1
2
3
4
5
@FunctionalInterface
public interface Runnable {
void test();
void check(); // 에러 발생
}

ch12-28 @SupressWarnings

  • 컴파일러의 경고 메시지가 나타나지 않게 억제 한다.
  • 괄호() 안에 억제 하고자 하는 경고의 종류를 문자열로 지정
1
2
3
@SuppressWarnings("unchecked")
ArrayList list = new ArrayList();
list.add(obj);
  • 둘 이상의 경고를 동시에 억제 하려면 다음과 같이 한다.
1
@SuppressWarnings ({"deprecation", "unchecked", "varargs"})

ch12-29 메타 애너테이션

  • 애너테이션 만들때 사용, java.lang.annotation 에 있음
  • @Target: 적용 대상 지정
  • @Retention: 유지 되는 기간 지정, SOURCE, CLASS, RUNTIME
  • @Documented: javadoc에 포함
1
2
3
4
5
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
  • @Inherited: 상속 하고자 할때 사용
1
2
3
4
5
6
7
@Inherited
@interface SuperAnno {}

@SuperAnno
class Parent {}

class child extends Parent {}
  • @Repeatable: 반복해서 붙일수 있음

ch12-34 애너테이션 타입 정의하기

1
2
3
4
@interface 애너테이션이름 {
타입 요소이름();
//..
}
  • 사용 시, 요소마다 값을 넣어 선언
  • default 값이 있는 경우도 생략 가능
  • 요소가 하나 이면 요소 이름 생략 가능
  • 요소의 타입이 배열 일 경우, 괄호 사용 {}
1
2
3
4
5
6
7
8
9
10
@interface TestInfo {
int count() default 1;
}

@TestInfo // @TestInfo (count=1) 과 동일
public class NewClass { ... }

@inteface TestInfo {
String[] testTools();
}

ch12-36 모든 애너테이션의 조상 - java.lang.annotation.Annotation

1
2
3
4
5
6
7
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();

Class<? extends Annotation> annotationType();
}

ch12-37 마커 애너테이션 - Marker Annotation

요소가 하나도 정의 되지 않는 애너테이션 예: @Test, @Deprecated

ch12-38 애너테이션 요소의 규칙

타입으로 기본형, String, enum, 애너테이션, Class만 허용됨
예외 선언 X, 괄호()안에 매개변수 선언 X, 타입 매개변수 X

1
2
3
4
5
6
@interface AnnoTest {
int id = 100;
String major (int i, int j); // 에러
String minor() throws Exception; // 에러
ArrayList<T> list(); // 에러
}

Annotation 선언 및 사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Retention(RetentionPolicy.RUNTIME)
@interface TestInfo {
int count() default 1;
String testedBy();
String[] testTools() default "JUnit";
TestType testType() default TestType.FIRST;
DateTime testDate();
}

@TestInfo(testedBy="mike", testTools={"JUnit", "JUnit5"},
testData=@DateTime(yymmdd="160101", hhmmss="235959"))
public static main(String args[]) {
Class<Ex12_8> cls = Ex12_8.class;
TestInfo anno = cls.getAnnotation(TestInfo.class);
anno.testedBy();
anno.testDate().yymmdd();
anno.testDate().hhmmss();

for(String str : anno.testTools())
System.out.println("testTools="+str);
}