Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
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
Tags more
Archives
Today
Total
관리 메뉴

프로그래밍 공부

[자바] #15. 열거형, 가변 인자, 어노테이션 본문

자바

[자바] #15. 열거형, 가변 인자, 어노테이션

하 냥 2025. 2. 19. 16:38

📢 열거형

📚 기본적인 enum 사용법

enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

public class EnumExample {
    public static void main(String[] args) {
        Day today = Day.MONDAY;
        System.out.println("오늘은: " + today);

        // switch문에서 enum 사용
        switch (today) {
            case MONDAY:
                System.out.println("월요일입니다.");
                break;
            case FRIDAY:
                System.out.println("불금입니다!");
                break;
            default:
                System.out.println("평범한 하루입니다.");
        }
    }
}

⚡ 열거형의 특징 및 내부 구조

열거형은 내부적으로 java.lang.Enum 클래스를 상속한다.

컴파일 시 다음과 같은 클래스로 변환된다:

final class Day extends Enum<Day> {
    public static final Day SUNDAY = new Day("SUNDAY", 0);
    public static final Day MONDAY = new Day("MONDAY", 1);
    // ...

    private Day(String name, int ordinal) {
        super(name, ordinal);
    }
}

✅ 기본 메서드

모든 열거형은 Enum 클래스의 다음 메서드를 자동 상속한다:

  • name() : 열거 상수의 이름 반환 (String)
  • ordinal() : 열거 상수의 정의된 순서(0부터 시작) 반환 (int)
  • values() : 모든 열거형 상수의 배열 반환
  • valueOf(String name) : 이름으로 상수를 반환

💡 기본 메서드 예제

public class EnumMethodsExample {
    enum Color { RED, GREEN, BLUE }

    public static void main(String[] args) {
        for (Color c : Color.values()) {
            System.out.println(c + "의 순서: " + c.ordinal());
        }

        Color color = Color.valueOf("RED");
        System.out.println("선택된 색상: " + color);
    }
}
RED의 순서: 0
GREEN의 순서: 1
BLUE의 순서: 2
선택된 색상: RED


🏗️ 열거형에 필드, 생성자 및 메서드 추가하기

열거형은 단순한 상수 이상의 기능이 가능하다.  필드, 생성자, 메서드를 정의하여 더 많은 기능을 제공할 수 있다.

// 인자를 전달받는 열거형

enum Season {
    SPRING("봄"), SUMMER("여름"), FALL("가을"), WINTER("겨울");

    private final String koreanName;

    // 생성자 (private으로 선언이 되어 직접 인스턴스를 생성하는 것은 불가)
    Season(String koreanName) {
        this.koreanName = koreanName;
    }

    // 메서드
    public String getKoreanName() {
        return koreanName;
    }
}

public class SeasonExample {
    public static void main(String[] args) {
        for (Season season : Season.values()) {
            System.out.println(season + " (" + season.getKoreanName() + ")");
        }
    }
}
SPRING (봄)
SUMMER (여름)
FALL (가을)
WINTER (겨울)

🌈 열거형에 추상 메서드 추가하기

enum Operation {
    PLUS {
        public int apply(int x, int y) { return x + y; }
    },
    MINUS {
        public int apply(int x, int y) { return x - y; }
    },
    MULTIPLY {
        public int apply(int x, int y) { return x * y; }
    },
    DIVIDE {
        public int apply(int x, int y) { return x / y; }
    };

    // 추상 메서드
    public abstract int apply(int x, int y);
}

public class OperationExample {
    public static void main(String[] args) {
        int a = 10, b = 5;
        for (Operation op : Operation.values()) {
            System.out.println(op + ": " + op.apply(a, b));
        }
    }
}
PLUS: 15
MINUS: 5
MULTIPLY: 50
DIVIDE: 2

🧩 enum과 인터페이스 구현

열거형도 인터페이스를 구현할 수 있다.

interface Printable {
    void print();
}

enum Shape implements Printable {
    CIRCLE {
        public void print() {
            System.out.println("원입니다.");
        }
    },
    SQUARE {
        public void print() {
            System.out.println("정사각형입니다.");
        }
    }
}

public class InterfaceEnumExample {
    public static void main(String[] args) {
        for (Shape s : Shape.values()) {
            s.print();
        }
    }
}

 

 


📢 가변 인자

가변 인자(Varargs, Variable Arguments)는 메서드가 임의의 수의 인자(argument)를 받을 수 있도록 하는 기능이다.

... (세 개의 점) 문법을 사용하여 정의한다.

 

✅ 간단한 예제

public class VarargsExample {
    static void printNumbers(int... numbers) {
        for (int number : numbers) {
            System.out.print(number + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        printNumbers(1);               // 하나의 인자
        printNumbers(1, 2, 3, 4, 5);   // 여러 개의 인자
        printNumbers();                // 인자 없이 호출
    }
}
1 
1 2 3 4 5

 

💡 가변 인자의 내부 동작

가변 인자는 내부적으로 배열로 처리된다.

예를 들어, int... numbers는 int[] numbers로 동작한다.

static void showArguments(String... args) {
    System.out.println("인자의 수: " + args.length);
    for (String arg : args) {
        System.out.println(arg);
    }
}

 

 

  • 여기서 showArguments("Hello", "World");는 내부적으로 다음과 같이 처리된다:
showArguments(new String[]{"Hello", "World"});

 

⚡ 가변 인자와 고정 매개변수 혼합 사용

가변 인자는 항상 메서드 매개변수 목록의 마지막에 위치해야 한다.

static void displayDetails(String name, int... scores) {
    System.out.println("이름: " + name);
    for (int score : scores) {
        System.out.println("점수: " + score);
    }
}

public static void main(String[] args) {
    displayDetails("Alice", 85, 90, 95);
}

 

 🧩 메서드 오버로딩과 가변 인자

가변 인자를 사용할 때 메서드 오버로딩이 헷갈릴 수 있다.

static void print(String s) {
    System.out.println("문자열: " + s);
}

static void print(String... strings) {
    System.out.println("가변 인자 호출: " + Arrays.toString(strings));
}

public static void main(String[] args) {
    print("Hello");              // 어떤 메서드가 호출될까?
    print("Hello", "World");     // 가변 인자 메서드 호출
}
문자열: Hello
가변 인자 호출: [Hello, World]

설명:

  • print("Hello")의 경우, 가장 정확히 일치하는 print(String s)가 호출된다.
  • print("Hello", "World")의 경우, print(String... strings)가 호출된다.

 

🌈 가변 인자와 배열의 관계

가변 인자 메서드는 호출 시 배열을 인자로 전달할 수도 있다.

static void showNumbers(int... numbers) {
    System.out.println(Arrays.toString(numbers));
}

public static void main(String[] args) {
    int[] nums = {1, 2, 3, 4};
    showNumbers(nums);  // 배열을 전달할 수 있음
}

내부 처리: showNumbers(nums)는 showNumbers(new int[]{1, 2, 3, 4});와 동일!

 

🏗️ 가변 인자 실전 예제

✅ 1. 문자열 연결 메서드

static String concatenate(String... strings) {
    StringBuilder result = new StringBuilder();
    for (String s : strings) result.append(s);
    return result.toString();
}

public static void main(String[] args) {
    System.out.println(concatenate("Hello", " ", "World", "!"));  // Hello World!
}

 

✅ 2. 평균 계산기

static double average(double... numbers) {
    if (numbers.length == 0)
    	return 0;
        
    double sum = 0;
    for (double n : numbers)
    	sum += n;
    return sum / numbers.length;
}

public static void main(String[] args) {
    System.out.println("평균: " + average(90.5, 85.0, 77.5));  // 평균: 84.3333...
}

 

 


📢 어노테이션

어노테이션(Annotation)은 자바 코드에 메타데이터를 제공하는 방법이다.

컴파일러, 개발 툴, 프레임워크 등이 어노테이션을 읽어 특정 행동, 검증, 처리 로직을 수행한다.

실행 시 영향이 없다.

 

💡 어노테이션의 역할

 

  • 컴파일러 지시: 컴파일 경고 억제(@SuppressWarnings), 오버라이드 체크(@Override)
  • 컴파일 타임 처리: 코드 생성(@Getter, @Setter - Lombok), 주입 처리(@Autowired - Spring)
  • 런타임 처리: 리플렉션(reflection)을 통해 런타임에 메타데이터 제공 (@Entity, @RequestMapping)
  • 문서화: Javadoc과 함께 사용하여 코드 설명 추가(@author, @version)

✍  자바 내장 어노테이션 종류

✅ 1) @Override

 

  • 메서드가 부모 클래스의 메서드를 오버라이드하는지 검사한다.
  • 컴파일 타임에서 오타나 잘못된 시그니처를 방지한다.
class Parent {
    void display() {}
}

class Child extends Parent {
    @Override
    void display() {  // 정확한 오버라이드 여부 확인
        System.out.println("자식 클래스 메서드");
    }
}

 

 

 

2) @Deprecated

 

  • 해당 요소(메서드, 클래스 등)가 더 이상 사용되지 않음을 표시한다.
  • 대체 기능이 있을 때 사용한다.
@Deprecated
void oldMethod() {
    System.out.println("이 메서드는 더 이상 사용되지 않습니다.");
}

 

 

3) @SuppressWarnings

  • 컴파일러 경고를 억제할 때 사용한다.
@SuppressWarnings("unchecked")
void uncheckedWarning() {
    List rawList = new ArrayList();
    rawList.add("Hello");
}

📌 자주 사용되는 경고 키워드:

  • "unchecked": 타입 검증 관련 경고
  • "deprecation": 사용 중인 deprecated 요소 경고
  • "rawtypes": 제네릭 미사용 경고

 4) @FunctionalInterface

  • 람다식 또는 메서드 참조에 사용할 수 있는 단일 추상 메서드 인터페이스임을 표시한다.
@FunctionalInterface
interface MyFunctionalInterface {
    void execute();  // 단 하나의 추상 메서드만 존재해야 함
}

 

 5) @SafeVarargs

  • 가변 인자 메서드에서 타입 안전성을 보장한다고 컴파일러에 알린다.
@SafeVarargs
static <T> void safeVarargsMethod(T... elements) {
    for (T element : elements) {
        System.out.println(element);
    }
}

 

🏗️ 메타 어노테이션

메타 어노테이션은 어노테이션을 정의할 때 사용되는 어노테이션이다.

어노테이션에 붙이는 어노테이션!

 

📚 주요 메타 어노테이션

메타 어노테이션 설명
@Target 어노테이션을 적용할 위치 지정 (클래스, 메서드, 필드 등)
@Retention 어노테이션의 유지 기간 지정 (SOURCE, CLASS, RUNTIME)
@Documented Javadoc 문서에 어노테이션 정보를 포함
@Inherited 하위 클래스가 부모 클래스의 어노테이션을 상속할 수 있도록 허용
@Repeatable 동일한 어노테이션을 여러 번 적용할 수 있도록 허용

 

✅ 1) @Target

어노테이션이 적용될 대상을 지정한다.

 

🎯 ElementType 종류:

  • TYPE: 클래스, 인터페이스, 열거형
  • METHOD: 메서드
  • FIELD: 멤버 변수
  • CONSTRUCTOR: 생성자
  • PARAMETER: 메서드 매개변수
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@interface MyAnnotation {}

@MyAnnotation // 적용 대상이 TYPE인 경우
class Main {
    @MyAnnotation // 적용 대상이 FIELD인 경우
    int num;
}

 

✅ 2) @Retention

어노테이션이 어느 시점까지 유지될지 지정한다.

 

🎯 RetentionPolicy 종류:

  • SOURCE: 소스 코드에만 존재하고 컴파일 시 제거됨 (예: @Override)
  • CLASS: 클래스 파일에 존재하지만 런타임 시 JVM에 로드되지 않음 (기본값)
  • RUNTIME: 런타임에도 유지되며, 리플렉션을 통해 접근 가능 (예: @Entity)
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface MyRuntimeAnnotation {}

 

✅ 3) @Documented

어노테이션에 대한 정보가 Javadoc으로 작성한 문서에 포함되게 한다.

Javadoc은 자바에서 지정한 형태의 주석들을 인식해서, HTML을 이용해 API 문서로 만들어주는 도구라 할 수 있다.

import java.lang.annotation.Documented;

@Documented
@interface MyDocumentedAnnotation {}

 

✅ 4) @Inherited

하위 클래스가 부모 클래스의 어노테이션을 상속할 수 있게 한다.

import java.lang.annotation.Inherited;

@Inherited
@interface InheritedAnnotation {}

@InheritedAnnotation
class Parent {}

// @InheritedAnnotation 어노테이션을 선언하지 않아도 선언한 것으로 인식! 
class Child extends Parent {}  // Child도 InheritedAnnotation을 상속받는다.

 

✅ 5) @Repeatable

동일한 어노테이션을 여러 번 적용할 수 있도록 한다.

import java.lang.annotation.*;

@Repeatable(Schedules.class)
@interface Schedule {
    String day();
    String time();
}

@Retention(RetentionPolicy.RUNTIME)
@interface Schedules {
    Schedule[] value();
}

@Schedule(day = "월요일", time = "10:00")
@Schedule(day = "화요일", time = "14:00")
class Meeting {}