본문 바로가기
아카이브/자바의 정석

12장 지네릭스,열거형,애노테이션 20201116

by nineteen 2020. 11. 16.
반응형

금요일에 멘탈이 털리고 토,일은 공부안하고 보냈다.

놀면서도 공부생각이 나서 괴로웠다.

공부해야하는데.. 하면서도 막상 보면 이해도 안가고 스스로한테 화나서 회피했다.

에혀

다시 월요일이니까 시작한다.

으아아ㅏ아아럅

 

 

열거형에 멤버 추가하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 열거형
enum Direction {
    EAST(1">"), SOUTH(2"V"), WEST(3"<"), NORTH(4"^");
    
    private static final Direction[] DIR_ARR = Direction.values();    // 배열로 반환
    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; }
    
    public static Direction of(int dir) {
        if(dir<1 || dir>4) {
            throw new IllegalArgumentException("Invalid value :" + dir);
        }
        return DIR_ARR[dir-1];
    }
    
    // 방향을 회전시키는 메소드, 90도씩 회전
    public Direction rotate(int num) {
        num = num%4;
        if(num<0) num += 4;
        return DIR_ARR[(value-1+num)%4];
    }
}
 
public class EnumEx2 {
    public static void main(String[] args) {
        for(Direction d : Direction.values()) 
            System.out.printf("%s=%d, %s\n", d.name(), d.getValue(), d.getSymbol());
        
        Direction d1 = Direction.EAST;    // 열거형의 참조변수에 EAST값 넣어줌
        Direction d2 = Direction.valueOf("WEST");    // 열거형의 참조변수에 WEST값 넣어줌
        
        System.out.printf("d1=%s, %d, %s\n", d1.name(), d1.getValue(), d1.getSymbol());
        System.out.printf("d2=%s, %d, %s\n", d2.name(), d2.getValue(), d2.getSymbol());
        System.out.println(Direction.SOUTH.rotate(1));
        System.out.println(Direction.SOUTH.rotate(2));
        System.out.println(Direction.SOUTH.rotate(3));
        System.out.println(Direction.SOUTH.rotate(4));
        System.out.println(d1.rotate(3));
    }
}
 
cs

출력

EAST=1, >
SOUTH=2, V
WEST=3, <
NORTH=4, ^
d1=EAST, 1, >
d2=WEST, 3, <
WEST
NORTH
EAST
SOUTH
NORTH

 

 

 

열거형 상수에 값을 추가하려면 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 추가해줘야 함

 

 

 

 

열거형에 추상 메소드 추가하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 열거형
enum Transportation {
    // 열거형 상수, 추상메소드를 구현
    BUS(100) { int fare(int distance) { return distance * BASIC_FARE; }},
    TRAIN(150) { int fare(int distance) { return distance * BASIC_FARE; }},
    SHIP(100) { int fare(int distance) { return distance * BASIC_FARE; }},
    AIRPLANE(300) { int fare(int distance) { return distance * BASIC_FARE;}};
    
    // 열거형 상수의 값에 대한 변수, protected로 해야 각 상수에서 접근가능
    protected final int BASIC_FARE;
    
    // 열거형 상수의 값에 대한 생성자
    Transportation(int basicFare) {
        BASIC_FARE = basicFare;
    }
    
    public int getBasicFare() { return BASIC_FARE; }
    
    // 추상메소드, 거리에 따른 요금 계산
    abstract int fare(int distance);
}
 
public class EunuEx3 {
    public static void main(String[] args) {
        // 거리 100에 대한 각 열거형 상수들의 요금계산
        System.out.println("bus fare="+Transportation.BUS.fare(100));
        System.out.println("train fare="+Transportation.TRAIN.fare(100));
        System.out.println("ship fare="+Transportation.SHIP.fare(100));
        System.out.println("airplane fare="+Transportation.AIRPLANE.fare(100));
    }
}
cs

출력

bus fare=10000
train fare=15000
ship fare=10000
airplane fare=30000

 

 

열거형에 추상 메소드를 선언하면 열거형 상수가 이 추상 메소드를 반드시 구현해야 함

 

 

 

 

열거형의 이해

열거형 Direction이 다음과 같이 정의되어 있을 때,

enum Direction { EAST, SOUTH, WEST, NORTH }

 

- 열거형 상수 하나하나가 Direction객체

- 클래스화 해보면?

1
2
3
4
5
6
7
8
9
10
11
12
class Direction {
    static final Direction EAST = new Direction("EAST");
    static final Direction SOUTH = new Direction("SOUTH");
    static final Direction WEST = new Direction("WEST");
    static final Direction NORTH = new Direction("NORTH");
 
    private String name;
    
    private Direction(String name) { 
        this.name = name;
    }
}
cs

위와 같다.

 

 

 

 

 

 

 

애노테이션 ( annotation )

- 미리 정의된 태그들을 이용해서 주석 안에 정보를 저장하고, javadoc.exe라는 프로그램이 이 정보를 읽어서 문서를 작성하는 데에 사용한다. 이러한 기능을 응용해, 프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이 애노테이션

- 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서 다른 프로그램에게 유용한 정보를 제공할 수 있다는 장점

-ex)

'@Test'라는 애노테이션을 메소드 앞에 붙이면, 해당 애노테이션이 붙은 메소드만 테스트가능.

'@Test'는 '이 메소드를 테스트 해야 함'을 테스트 프로그램에게 알리는 역할을 담당, 메소드가 포함된 프로그램 자체에는 영향X

 

 

표준 애노테이션

- 자바에서 기본적으로 제공하는 애노테이션들은 몇 개 없는데, 이 중 일부는 '메타애노테이션'으로 애노테이션을 정의하는데 사용되는 애노테이션의 애노테이션

 

@Override

- 메소드 앞에만 붙일 수 있는 애노테이션

- 조상의 메소드를 오버라이딩하는 것이라는 걸 컴파일러에게 알려주는 역할

 

@Deprecated

- 더 이상 사용되지 않는 필드나 메소드에 붙임

- 이 애노테이션이 붙은 대상은 다른 것으로 대체되었으니 더 이상 사용하지 않는 것을 권한다는 의미

- '@Deprecated'가 붙은 대상을 사용해도 컴파일은 정상적으로 실행

 

@FunctionalInteface

- '함수형 인터페이스'를 선언할 때 사용

- 이 애노테이션을 붙이면 컴파일러가 '함수형 인터페이스'를 올바르게 선언했는지 확인

 

@SuppressWarnings

- 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제해주는 역할

- '@SuppressWarnings'로 억제할 수 있는 경고 메시지의 종류로 주로 사용되는 것은 'deprecation', 'unchecked', 'rawtypes', 'varargs'등

'deprecation' : '@Deprecated'가 붙은 대상을 사용해서 발생 하는 경고를 뜻함

'unchecked' : 지네릭스로 타입을 지정하지 않았을 때 발생하는 경고

'rawtypes' : 지네릭스를 사용하지 않아서 발생하는 경고

'varargs' : 가변인자의 타입이 지네릭 타입일 때 발생하는 경고를 억제할 때 사용

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.ArrayList;
 
class NewClass {
    int newField;
    
    int getNewField() { return newField; }
    
    @Deprecated
    int oldField;
    
    @Deprecated
    int getOldField() { return oldField; }
}
 
public class AnnotationEx2 {
    @SuppressWarnings("deprecation")    // deprecation관련 경고를 억제
    public static void main(String[] args) {
        NewClass nc = new NewClass();
        
        nc.oldField = 10;                        // @Deprecated가 붙은 대상 사용, 경고발생
        System.out.println(nc.getOldField());    // @Deprecated가 붙은 대상 사용, 경고발생
        
        @SuppressWarnings("unchecked")    // 지네릭스 관련 경고를 억제
        ArrayList<NewClass> list = new ArrayList();    // 타입 지정x, 경고발생
        list.add(nc);
    }
}
cs

 

 

@SuppressWarnings를 합쳐서 선언 할 수 도 있다

1
2
@SuppressWarnings({"deprecation""unchecked"})    // deprecation과 unchecked 관련 경고를 억제
 
cs

 

 

 

@SafeVarargs

- 메소드에 선언된 가변인자 타입이 non-reifiable타입일 경우, 해당 메소드의 선언, 호출부분에서 'unchecked'경고 발생

- 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 이 애노테이션을 사용

- static이나 final이 붙은 메소드와 생성자에만 붙일 수 있음 ( 오버라이드될 수 있는 메소드에는 사용 불가 )

 

reifiable, non-reifiable타입?

- 지네릭스에서 살펴봤듯이 어떤 타입들은 컴파일 이후에 제거되는데, 컴파일 후에도 제거되지 않는 타입을 reifiable타입, 제거되는 타입을 non-reifiable타입

- 지네릭 타입들은 대부분 컴파일 시에 제거되므로 non-reifiable타입

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.Arrays;
 
class MyArrayList<T> {
    T[] arr;
    
    @SafeVarargs                    // unchecked경고 억제
    @SuppressWarnings("varargs")    // varargs경고 억제, 이 애노테이션으로 unchecked를 억제하려면 선언,호출부분에 선언해야함
    MyArrayList(T... arr) {            // 가변인자
        this.arr = arr;
    }
    
    @SafeVarargs
//    @SuppressWarnings("unchecked")
    public static <T> MyArrayList<T> asList(T... a) {
        return new MyArrayList<>(a);
    }
    
    public String toString() {
        return Arrays.toString(arr);
    }
}
 
public class AnnotationEx4 {
//    @SuppressWarnings("unchecked")    
    public static void main(String[] args) {
        MyArrayList<String> list = MyArrayList.asList("1""2""3");
        System.out.println(list);
    }
}
cs

12행 주석처리, 13,24행 주석해제 -> 같은 결과

 

 

 

 

 

 

 

메타 애노테이션

- 애노테이션을 위한 애노테이션

- 애노테이션에 붙이는 에노테이션으로, 애노테이션을 정의할 때 애노테이션의 적용대상이나 유지기간등을 지정하는 데에 사용

 

 

@Target

- 애노테이션이 적용가능한 대상을 지정하는데 사용

- ex)

'@SuppressWarnings'에 적용할 수 있는 대상을 '@Target'으로 지정한다면?

1
2
3
4
5
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
cs

 

@Retention

- 애노테이션이 유지되는 기간을 지정하는데 사용

- 애노테이션의 유지정책의 종류는 아래와 같다

SOURCE : 소스 파일에만 존재. 클래스파일에는 존재X

CLASS : 클래스 파일에 존재. 시행시에 사용불가. 기본값

RUNTIME : 클래스 파일에 존재. 실행시에 사용가능

 

@Documented

- 애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 함

- 자바에서 제공하는 기본 애노테이션 중에 '@Override', '@SuppressWarnings'를 제외하고는 모두 이 메타 애노테이션이 붙어 있음

 

@Inherited

- 애노테이션이 자손 클래스에 상속되도록 함

- '@Inherited'가 붙은 애노테이션을 조상클래스에 붙이면, 자손 클래스도 이 애노테이션이 붙은 것과 같이 인식 됨

 

@Repeatable

- 보통은 하나의 대상에 한 종류의 애노테이션을 붙이는데, '@Repeatable'이 붙은 애노테이션은 여러 번 붙일 수 있음

- 같은 이름의 애노테이션이 여러 개가 하나의 대상에 적용될 수 있기 때문에 이 애노테이션들을 하나로 묶어서 다룰 수 있는 애노테이션을 추가로 정의해야함 ( 이 애노테이션을 @Repeatable로 지정해야함 )

 

@Native

- 네이티브 메소드에 의해 참조되는 '상수필드'에 붙이는 애노테이션

- 네이티브 메소드는 JVM이 설치된 OS의 메소드를 의미함

- Object클래스의 메소드들이 대부분 네이티브 메소드

- 네이티브 메소드를 호출하는 방법은 일반 메소드와 같지만 실제로 호출되는 것은 OS의 메소드

 

 

 

 

 

애노테이션 타입 정의하기

- 새로운 애노테이션을 정의하는 방법은 아래와 같다

인터페이스를 정의하는 것과 동일하며, '@'기호를 붙이면 됨

ex)

@interface 애노테이션이름 {

    타입 요소이름(); // 애노테이션의 요소 선언

    ...

}

 

애노테이션의 요소

- 애노테이션 내에 선언된 메소드를 '에노테이션의 요소'라고 함

- 반환값이 있고 매개변수는 없는 추상메소드의 형태이며, 상속을 통해 구현하지 않아도 됨

- 애노테이션을 적용할 때, 이 요소들의 값을 전부 지정해주어야 함

- 요소의 이름도 같이 적으므로 순서는 상관x

 

java.lang.annotation.Annotation

- 모든 애노테이션의 조상은 Annotation

- 상속이 허용되지 않아 extends로 조상 지정 불가

 

마커 애노테이션 Marker Annotation

- 애노테이션의 요소를 하나도 정의하지 않을 수 있음 (ex. Serializable, Cloneable인터페이스)

- 요소가 하나도 정의되지 않은 애노테이션

 

애노테이션 요소의 규칙

- 요소의 타입은 기본형, String, enum, 애노테이션, Class만 허용

- ()안에 매개변수 선언 불가

- 예외 선언 불가

- 요소를 타입 매개변수로 정의 불가

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
 
// 열거형
enum TestType { FIRST, FINAL }
 
@Retention(RetentionPolicy.RUNTIME)    // 메타애노테이션 
@interface DateTime {                // 새로운 애노테이션 정의
    // 애노테이션 요소
    String yymmdd();
    String hhmmss();
}
 
@Retention(RetentionPolicy.RUNTIME)    // 실행 시에 사용가능하도록 설정
@interface TestInfo {                // 새로운 애노테이션 정의
    // 애노테이션 요소
    int count() default 1;
    String testedBy();
    String[] testTools() default "JUnit";
    TestType testType() default TestType.FIRST;
    DateTime testDate();
}
 
@Deprecated                    // 앞으로 사용하지 않을 것이라는 의미
@SuppressWarnings("1111")    // 
// 애노테이션 적용 시, 요소들의 값을 지정해주고 이름도 적어야함
@TestInfo(testedBy="aaa", testDate=@DateTime(yymmdd="201116", hhmmss="171725"))
public class AnnotationEx5 {
    public static void main(String[] args) {
        Class<AnnotationEx5> cls = AnnotationEx5.class;                    // AnnotationEx5의 Class객체를 얻음
        
        TestInfo anno = (TestInfo)cls.getAnnotation(TestInfo.class);    // AnnotationEx5에 적용된 @TestInfo의 정보를 얻음
        System.out.println("anno.testedBy()="+anno.testedBy());
        System.out.println("anno.testDate().yymmdd()="+anno.testDate().yymmdd());
        System.out.println("anno.testDate().hhmmss()="+anno.testDate().hhmmss());
        
        for(String str : anno.testTools()) 
            System.out.println("testTools="+str);
        System.out.println();
        
        // AnnotationEx5에 적용된 모든 애노테이션을 가져옴
        Annotation[] annoArr = cls.getAnnotations();
        
        for(Annotation a : annoArr)
            System.out.println(a);
    }
}
 
cs

출력

anno.testedBy()=aaa

anno.testDate().yymmdd()=201116

anno.testDate().hhmmss()=171725

testTools=JUnit

 

@java.lang.Deprecated()

@annotation.TestInfo(count=1, testType=FIRST, testTools [JUnit], testedBy=aaa, testDate=@annotation.DateTime(yymmdd=201116, hhmmss=171725))