프로그래밍 공부
[자바] #15. 열거형, 가변 인자, 어노테이션 본문
📢 열거형
📚 기본적인 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 {}
'자바' 카테고리의 다른 글
| [자바] #17. 람다(Lambda)와 함수형 인터페이스 (1) | 2025.02.19 |
|---|---|
| [자바] #16. 네스티드(Nested) 클래스, 이너(Inner) 클래스 (0) | 2025.02.19 |
| [자바] #14. Collections 클래스 안의 여러 알고리즘 구현 메서드들 (0) | 2025.02.19 |
| [자바] #13. 컬렉션 프레임워크 - 4. Map (0) | 2025.02.16 |
| [자바] #12. 컬렉션 프레임워크 - 3. Queue, Deque (0) | 2025.02.16 |