4/26 enums, 애너테이션

박세현·2024년 4월 26일

JAVA

목록 보기
15/22
post-thumbnail

열거형(enums)

1. 열거형이란?

  • 상수만을 위한 클래스 일종
  • 자바 8버전부터 많이 사용



2. 열거형 정의와 사용

enum 클래스명 {
   상수, 상수, 상수...
}
  • 상수간의 비교 ==
    -> 상수니까 1개만 존재 = 주소가 항상 동일하다
    -> equals() 안써도 된다는 말

참고)
상수 : 유일하게 1개 존재
-> 항상 주소가 동일하다
-> 객체 생성과 상관없이 접근 가능하다
-> 단, enum 클래스에 정의한 멤버변수는 이미 객체이며 정적 상수 형태이다
-> enum 클래스에 정의한 멤버변수 : 객체⭕ + final + static


예시) enum 클래스

ㄴ enum클래스 정의

ㄴ 상수다 보니 객체 생성 상관 없이 클래스명으로 바로 접근 가능



예시) 상수니까 1개만 존재 = 주소가 항상 동일하다

ㄴ enum클래스 정의

ㄴ 상수니까 1개만 존재 = 주소가 항상 동일하다



예시) switch~case구문

ㄴ 자료형이 뭔지 예상 할 수 있기 때문에 switch~case구문에서는 enum상수의 자료형 명시 안해도ㅇㅋ




3. enum클래스 특징

1) 모든 열거형의 상위 클래스 - java.lang.Enum

  • 다형성
  • 모든 enum클래스는 java.lang.Enum의 하위클래스이다
  • 모든 enum클래스는 java.lang.Enum에 정의된 메서드 모두 사용가능하다


2) enum클래스에 정의한 멤버변수는 이미 객체이며 정적 상수 형태이다

  • enum 클래스에 정의한 멤버변수 : 객체⭕ + final + static
  • 내부적으로 안보이지만 정적상수형태로 객체 생성 로직이 들어가 있음

예시) 1), 2) 적용 예시

public enum Transportation {
    BUS, //Transportation의 객체, 정적 상수 형태
    SUBWAY,
    TAXI
}

↓ 내부적으로 이렇게 되어있다

abstract class Transportation extends java.lang.Enum {
	public static final BUS = new Transportation();
	public static final SUBWAY = new Transportation();
	public static final TAXI = new Transportation();

private Transportation(){};

}


3) enum클래스의 생성자의 접근제어자는 private

  • enum클래스 : 상수만을 위한 클래스
  • 외부에서 enum클래스를 객체로 생성 시 객체가 여러번 만들어 질 수 있기 때문에 상수로서의 의미 상실
  • enum클래스의 생성자함수의 접근제어자를 private으로 정의해 외부에서 enum클래스 객체를 생성하는 걸 통제함

  • enum클래스에 인스턴스변수 정의해도 되지만 final로 정의하는게(상수로 정의하는게) 관례
    ex) private final int fare;
  • enum클래스에 인스턴스메서드도 정의 가능
    ex) get()메서드...


예시)

ㄴ 내부적으로 생성자 함수가 enum상수를 객체로 만들어 주고 있넹
ㄴ Transportation클래스(enum클래스)도 클래스니까 기본생성자함수가 내부적으로 정의되어 있을 것이다

ㄴ 잉? 왜 Transportation클래스(enum클래스)에 기본생성자 함수호출이 안될까 = 왜 Transportation클래스(enum클래스)를 객체로 만들 수 없지?
-> Transportation클래스(enum클래스)를 객체로 만들게 되면 상수로서 가치가 없어짐
-> 상수는 유일해야함, 1개만 존재
-> 근데 직접 객체 생성하게되면 생성자 오버로드에 의해서 객체가 여러개 생성될 수 있잖슴
-> 상수로서의 의미가 상실됨 (유일하지 않음)
-> 그래서 enum클래스의 생성자함수의 접근제어자를 private으로 정의해 외부에서 enum클래스 객체를 생성하는 걸 통제함

ㄴ enum클래스의 기본생성자의 접근제어자는 private



예시2)

ㄴ enum클래스에 접근제어자 명시안하고 기본생성자 함수 정의함
ㄴ 원래는 접근제어자 명시 안하면 default가 접근제어자 이지만 enum클래스는 무조건 생성자함수의 접근제어자는 private이다

ㄴ 증거 : 외부에서 객체 생성 시 에러 뜸
ㄴ 에러뜨는 이유 : 생성자함수의 접근제어자가 private



예시3) enum클래스에 정의된 멤버변수에 매개변수 넣기

ㄴ 에러 뜸 왜?
BUS(...) : 생성자를 호출한거랑 같음

public enum Transportation {
    BUS(1400),  // 생성자를 가리킴 -> Transportation() {}
    SUBWAY(4500),  // 생성자를 가리킴 -> Transportation() {}
    TAXI(1300);  // 생성자를 가리킴 -> Transportation() {}

    Transportation() {
        
    }

ㄴ 매개변수가 있는 생성자를 호출했는데 기본생성자에 매개변수가 없으니까 에러가 뜬 것

ㄴ 요거 참고하면 왜 BUS(...) 가 생성자를 호출한거 인지 이해 됨ㅇㅇ
ㄴ 자 그럼 에러 가 뜬 원인이 기본생성자에 매개변수가 없어서니까 매개변수 넣어주러 가보장

ㄴ enum클래스에 인스턴스변수 정의해도 되지만 final로 정의하는게(상수로 정의하는게) 관례
final 변수 = 상수 : 상수는 값을 꼭 초기화 해붜야함
ㄴ 지금 초기화 안해서 에러 뜬거

private final int fare; 초기화 해 줌

ㄴ get() 함수 정의

ㄴ BUS가 객체니까 getFare()함수에 접근 가능 하군
ㄴ 객체의 자원 = 변수 와 함수 -> 객체가 되면 객체의 자원인 변수와 함수 이용가능

ㄴ 값 조회해보자

ㄴ 값이 잘 나왔넹



4) Enum 클래스는 본질이 추상클래스이다

  • 추상클래스라서 추상메서드 정의 가능하다
  • 열거형에 추상 메서드를 선언하면 각 열거형 상수가 이 추상 메서드를 반드시 구현해야 한다.


예시)

ㄴ {} = 구현내용을 안넣어서 에러 뜬거

BUS(1350) {
	public int getTotal(int person) {
    	return getFare() * person;
    }
}

ㄴ 이런식으로 구현 내용 넣어주면 에러해결 될 거임
-> 근데 이렇게는 비효율적이어서 잘 안쓴다고 함
-> enum클래스가 추상클래스라는걸 보여주기 위해 하드코딩한거



예시)

ㄴ 미구현된메서드라 에러 뜸

ㄴ {} = 구현내용 넣어주면 해결 완




4. Enum 클래스에 정의된 메서드

참고) 자바문서_Enum
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html

1) final String name()

  • Enum 상수이름을 문자열로 변환

    • DB는 자바객체 인식 못 함 , 자바객체는 자바만 인식 가능
    • Enum상수는 자바 객체
    • 즉, 상수를 인식 못해서 DB가 인식 할 수 있는 문자열로 변환해줘야 함
  • String toString()과 동일


예시) name()

ㄴ enum클래스 정의

ㄴ Enum 상수이름을 문자열로 변환



2) final int ordinal()

  • enum상수의 위치 번호

예시)

ㄴ enum클래스 정의

ㄴ enum상수 TAXI의 위치번호 출력



3) static valueOf(클래스클래스, String name)

  • 변환 메서드
  • 문자열을 상수로 변환
  • DB, 웹...등에는 문자열로 데이터가 있음
    -> 그럼 다시 자바가 인식 할 수 있는 상수로 변환
  • 매개변수에 클래스클래스가 들어가는 이유
    -> 클래스의 정보를 참고하기 위해서
    -> 어느 클래스의 tran1을 가져올지 알려주는 것

예시)



4) 컴파일러가 자동으로 추가해주는 메서드

① valueOf(String str)

  • 문자열을 상수로 변환
  • 각각의 상수객체에서 접근하면 자료형이 뭔지 특정 가능
  • 호출 객체가 Enum 상수이므로 Class 객체는 필요 x
    = 호출객체가 Enum 상수이므로 어느 클래스에서 가져올지 알려주는 거나 다름없음
    = static valueOf(클래스클래스, String name) 과 다르게 클래스클래스 매개변수로 안 넣어도 ㅇㅋ
  • static valueOf(클래스클래스, String name) = valueOf(String str)
    -> 코드짜는 방식이 다를 뿐 같은 기능을 함
    -> 사용빈도 : static valueOf(클래스클래스, String name) < valueOf(String str)
  • Enum 상수의 정적 메서드 추가

예시)

static valueOf(클래스클래스, String name) = valueOf(String str)
ㄴ 코드짜는 방식이 다를 뿐 같은 기능을 함



② values()

  • 정의된 상수 목록을 배열로 반환

예시)





6. Enum 클래스의 특징 4가지 정리

1) 정의하자마자 java.lang.Enum의 하위클래스이다


2) 정의하자마자 정적 상수 객체

  • final + static + 객체⭕

3) 생성자를 프라이빗으로 정의함

  • 상수로서 역할을 해야 함, 객체를 막 만들면 안됨
  • ;으로 마무리해주고 기본생성자 정의 가능

4) Enum 클래스는 본질이 추상클래스이다

  • (컴파일러가 알아서 바꾼다) 추상메서드 정의 가능하다




애너테이션(annotation)

  • 정보를 전달하기 위해 목적이 정해진 클래스의 일종
  • 클래스의 구성요소(클래스 정의의 일부분)
    ㄴ 클래스클래스 객체에서
    ㄴ 인터페이스의 추상메서드로 구현되어 있음
    ㄴ 구현체가 있다
    ㄴ 객체의 구성 요소
    ㄴ 반환값
    ㄴ 내부적으로 객체가 생성

참고) 자바문서_Annotation
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/annotation/Annotation.html



1. 에너테이션이란?

  • 주석, 주해...
  • 정보 전달이 목적
  • 클래스의 구성요소
    -> 에노테이션을 조회할려면 클래스클래스객체가 필요하다
    -> 클래스클래스 : 클래스의 구성요소 정보를 담고 있음 ⊃ 에노테이션

참고) java.lang.class

ㄴ 그래서 자바문서 java.lang.class에 에노테이션 확인 가능 메서드 있음
ㄴ 매개변수 클래스클래스 -> getAnnotaion(클래스명.class)



1) 모든 annotation은 annotation.Annotation인터페이스의 하위자료형이다

@interface 애노테이션 명 {

}

↓ 내부적으로 이렇게 바껴 있다

interface 애노테이션명 extends java.lang.annotation.Annotation {

}



2. 표준 애너테이션

  • JDK에 이미 정의 된 애노테이션


1) @Override

  • 재정의 된 메서드 임을 알려주는 목적
  • 해당 메서드가 슈퍼 클래스의 메서드를 오버라이드하는지 체크


2) @Deprecated

  • 향 후 제거될 가능성이 있는 메서드임을 알려주는 목적
  • 해당 요소(클래스, 메서드 등)가 더 이상 권장되지 않음을 알려줌


3) @SuppressWarnings

  • 컴파일러 경고를 억제함


4) @FunctionalInterface

  • 함수형 인터페이스임을 알려줌



3. 메타 애너테이션

  • 에노테이션을 정의할 때 필요한 에노테이션
  • 에노테이션을 만들 수 있는 에노테이션
  • @Target, @Retention : 에노테이션을 정의할 때 필수적인 에노테이션


1) @Target

  • 에노테이션 적용 범위
    • ElementType : 적용범위 enum 상수
      • METHOD : 메서드 위에 적용
      • TYPE : 클래스명 위에 적용
      • FEILD : 멤버변수 위에 적용
      • CONSTRUCTOR : 생성자 위에 적용
      • PARAMETER : 매개변수 앞에 적용
      • LOCAL_VARIABLE : 지역변수
      • TYPE_PARAMETER : 참조변수
      • TYPE_USE : 참조자료형

예시)

↓ @Override 컨트롤 + 마우스 왼쪽키



2) @Retention

  • 정보 전달 시점

    • RetensionPolicy

      • SOURCE
        : 애노테이션이 java파일 상에 유지, 컴파일 완료 후(class 파일)에는 제거
        - 컴파일러가 필요한 정보를 제공
        - 컴파일러가 필요한 정보

      • CLASS : 기본값
        : 에노테이션이 java파일 상에 유지, 컴파일 완료 후 유지
        - 정보 전달x
        - 기본값임에도 사용x

      • RUNTIME
        : 애노테이션이 java파일 상에 유지, 컴파일 완료 후(class 파일) 유지
        - 정보전달이 실행 중에 제공


예시)

ㄴ A클래스에 메서드 정의함

ㄴ B클래스가 A클래스에 상속받음
ㄴ B클래스가 A클래스에 정의된 메서드 오버라이드(메서드재정의)함
↓ @Override 컨트롤 + 마우스 왼쪽키

↓ RetensionPolicy 컨트롤 + 마우스 왼쪽키



① 마커에노테이션

  • 단순 정보 전달 목록(설정x) = 추상메서드 정의❌
    ex) @Override : 재정의된 메서드라고 단순히 정보만 달

예시)

ㄴ A클래스에 메서드 정의함

ㄴ B클래스가 A클래스에 상속받음
ㄴ B클래스가 A클래스에 정의된 메서드 오버라이드(메서드재정의)함
↓ @Override 컨트롤 + 마우스 왼쪽키

public @interface Override {
}

ㄴ 인터페이스 안쪽에 정의된 내용이 하나도 없음
ㄴ ② 추상 메서드 정의에 있는 예시 연결해서 보기



② 추상 메서드 정의

  • 에노테이션도 추상메서드를 정의할 수 있다

  • 에노테이션에서 추가 정보 전달 방식 = 추상 메서드 정의

  • 추가 정보= 설정항목

  • 추상 메서드 정의 = 설정항목들을 주로 정의

  • 에노테이션 조회

    • getAnnotaion(클래스명.class)
    • getAnnotaions()
  • 설정항목이 추가되면 무조건 설정을 추가해 줘야 함(값을 넣어줘야 함)
    ex)

    public @interface 에노테이션클래스명 {
    int min(); // min 설정 항목
    int max(); // max 설정 항목
    }
    
    ↓ 
    
    @에노테이션클래스명(min=10, max=100) // 설정 추가
    
  • 클래스가 로드 될 때 에노테이션 추상메서드의 구현객체가 설정값을 담은 상태로 객체가 만들어진다
    = 에노테이션 추상메서드는 클래스가 로드 될 때 설정추가 한 값을 반환값으로한 객체가 생성된다

  • default : 설정항목에 설정추가 안하고 기본값 넣기
    -> 기본값이 있으면 설정추가 안해도 ㅇㅋ
    -> 기본값이 없으면 필수로 설정추가해줘야 함(값을 넣어줘야 함)
    ex)

    public @interface MyAnno {
            int min() default 10; // min() 설정 항목, 기본값 10
            int max() default 100;// max() 설정 항목, 기본값 100
        }
  • 설정추가가 한개만 있는 경우 value()를 많이 쓴다
    -> 설정추가 할 때 생략 가능하기 때문에
    ex)

String value(); // 에노테이션 추상메서드 정의(설정항목)

↓ 

@MyAnno(value = "이름1") //이렇게 설정항목에 설정추가해주는게 정석이지만

↓

@MyAnno("이름1") // 설정추가가 한개만인경우 이렇게 생략가능

예시)
↓ @Target 컨트롤 + 마우스 왼쪽키

public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation interface
     * can be applied to.
     * @return an array of the kinds of elements an annotation interface
     * can be applied to
     */
    ElementType[] value();
}

ㄴ 인터페이스 안쪽에 정의된 내용이 있음
ㄴ 에노테이션도 추상메서드를 정의할 수 있다
ㄴ 설정항목들을 주로 정의



예시) 에노테이션 만들어보기

ㄴ 에노테이션 클래스 생성

ㄴ 인터페이스이자 클래스이다

ㄴ 에노테이션 만들기 완

ㄴ 내가 만든 에노테이션(MyAnno) 적용해보기
ㄴ 에노테이션 적용 범위 늘리고 싶은뎅

ㄴ MyAnno 에노테이션클래스에 정의한 @Target(ElementType.Type) 에서 ()쪽에 입력된 값은 value()에 할당된 것
ㄴ value() : 설정에대한 이름
ElementType[] value(); : 배열 형태로 되어있다 = 설정항목을 늘릴 수 있음

참고) 배열형태가 아니면 설정 한개만 가능
↓ @Retention 컨트롤 + 마우스 왼쪽키

@Target(ElementType.Type) -> @Target(value={ElementType.Type, ElementType.METHOD})
ㄴ 에노테이션 적용 범위 늘리기 완

ㄴ 코드 줄이기
@Target({ElementType.Type, ElementType.METHOD}) : value 생략 가능
import static java.lang.annotation.ElementType.*; : import하면 매번 ElementType안붙이고 상수명만으로도 ㅇㅋ

ㄴ 에노테이션 적용 범위 늘어난거 확인 완



예시) 에노테이션에 추상 메서드 정의 + 에노테이션 객체 생성(클래스 로드시점)

ㄴ MyAnno 에노테이션에 int min();, int max();추상메서드 정의
-> int min(); : min 설정 항목
-> int max(); : max 설정 항목
-> 설정항목이 추가되면 무조건 설정을 추가해 줘야 함

ㄴ 설정항목(min, max)을 포함해서 설정 추가 함 = 정보를 전달
ㄴ 정보를 전달 했으니 확인 할 수 있어야 겠징
ㄴ 정보 전달 시점 : RUNTIME으로 정의함 -> 실행과정중에 확인 가능함
ㄴ 에노테이션도 클래스 정의의 일부(클래스의 구성요소) = 클래스 안에 에노테이션 정의함 = 클래스클래스 객체에서 에노테이션 확인가능

ㄴ 그래서 자바문서 java.lang.class에 에노테이션 확인 가능 메서드 있음
ㄴ 매개변수 클래스클래스 -> getAnnotaion(클래스명.class)

MyAnno anno = (MyAnno)cls.getAnnotation(MyAnno.class);
ㄴ 에노테이션 정보를 한번 조회해보자
ㄴ 상위-> 하위클래스니까 강제형변환 해주기

ㄴ @MyAnno에노테이션은 추상메서드에 불과 = @MyAnno(min=10, max=100) 에서 10, 100은 설정만 한 것 뿐이지 아직 이 값이 반환값인건 아님
-> 실제 반환값이 있게 만들어 질려면 객체가 되야 함
-> 로드 될 때 에노테이션의 구현객체가 만들어진다
ㄴ 클래스를 컴파일 하고 실제 로딩이 됬을 때 프록시가 에노테이션의 구현객체를 대신해서 만들어 줌
ㄴ 프록시 : 대신해서 완성된 구현객체 생성
ㄴ 메서드에 값이 있다 = 메서드가 완전하다

int min = anno.min();, int max = anno.max(); : 호출해야 에노테이션 추상메서드에 담겨있는 값이 에노테이션 추상메서드 구현체의 반환값이 됨
ㄴ 출력되는거 보니까 반환값이 있넹
ㄴ MyAnno는 추상메서드인데 어떻게 반환값이 있지?
-> 반환값이 있다 = 메서드 구현내용이 있다
= 반환값은 메서드의 구현부분에 있으니까 반환값이 있다는 것은 메서드의 구현내용이 있다는 뜻
-> 반환값이 나올 수 있게 MyAnno 추상메서드에 구현내용이 추가되었다
-> 클래스가 로드 될 때 프록시형태의 어떤 객체로 MyAnno 추상메서드의 구현객체가 설정값@MyAnno(min=10, max=100)을 담은 그 상태로 만들어진다



예시) default : 설정하지 않고 기본값 넣기

ㄴ default : 설정하지 않고 기본값 넣기

ㄴ 기본값을 설정했기 때문에 설정추가 안해도 에러 안 뜸

ㄴ default로 넣은 기본값이 출력되는 것을 볼 수 있음



예시) 설정값이 여러개인 경우 -> 배열로 정의

ㄴ 배열형태로 에노테이션 추상메서드 정의하면 설정값을 여러개 넣을 수 있음
ㄴ default로 기본값 설정 안했으니 설정 추가 즉, 값을 넣어줘야 함
-> 배열이니까 배열형태로 값 넣어주기
-> String으로 정의했으니 문자형태로 값 넣기

참고) 배열에서 default 설정 형식

ㄴ String자료형 + 배열형태로 설정 추가 해 줌 = 값들을 넣어줌

참고)
설정추가할 값이 한개만 있으면 {}로 생략가능

ㄴ 조회해보자
ㄴ 출력 잘 되었군ㅎㅎ



예시) 설정추가가 한개만 있는 경우 value()를 많이 쓴다

String value(); : 에노테이션 추상메서드 정의

ㄴ 원래는 이렇게 하는게 맞음

@MyAnno(value = "이름1") -> @MyAnno("이름1")
ㄴ value() 의 설정추가가 한개만 있는 경우 value= 생략가능
ㄴ 두개이상이면 생략x
ㄴ 그래서 설정추가가 한개만 있는 경우 value()를 많이 쓴다

참고) 설정추가 2개 -> value= 생략x

ㄴ 생략했더니 에러 뜸

ㄴ ㅇㅋㅇㅋ

ㄴ 출력 잘 됨



3) @Repeatable

예시) @Repeatable

ㄴ todos 에노테이션 클래스 정의

ㄴ todo 에노테이션 클래스 정의
ㄴ 에러 뜨는 이유 : 설정이 필수로 한개 들어가야 함

↓ Repeatable 마우스 우클릭

ㄴ @Repeatable : 매개변수가 클래스클래스
ㄴ 즉 반복해서 담아 줄 에노테이션이 뭔지 알려줘야 한다

ㄴ @Repeatable 이기 때문에 반복해서 값을 담을 수가 있음
ㄴ 값의 조회는 Todos를 통해 가능

ㄴ 할일1, 할일2, 할일3 출력 됨





profile
귤귤

4개의 댓글

comment-user-thumbnail
2024년 6월 18일

안녕하세요. 미슐랭가이드 심사위원입니다.
⭐️⭐️⭐️드리겠습니다.

1개의 답글