ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] #5 제네릭(Generics), 이넘(Enum), 어노테이션(@, Annotation)
    Software engineer/Java 2023. 5. 1. 11:26

     

    제네릭(Generic)

    제네릭은 클래스나 메서드에서 사용할 데이터 타입을 컴파일 시에 지정할 수 있도록 하는 기능입니다. 제네릭을 사용하면 클래스나 메서드를 재사용하기 쉬워지며, 타입 안정성(type safety)을 보장할 수 있습니다.

     

    컬렉션(Collection)

    Java에서 제공하는 대부분의 컬렉션은 제네릭을 사용합니다. 제네릭을 사용하지 않은 컬렉션은 Object 타입으로 요소를 저장하기 때문에, 요소를 꺼낼 때 타입 캐스팅이 필요합니다. 이렇게 타입 캐스팅을 하면 코드의 가독성과 안정성이 떨어지며, 오류 발생 가능성도 높아집니다.

    List list = new ArrayList();
    list.add("hello");
    String str = (String) list.get(0); // 타입 캐스팅 필요

    반면, 제네릭을 사용하면 타입 안정성을 보장하면서 코드의 가독성을 높일 수 있습니다.

    List<String> list = new ArrayList<>();
    list.add("hello");
    String str = list.get(0); // 타입 캐스팅 불필요

     

    메서드(Method)

    제네릭을 사용하면 메서드를 보다 유연하게 작성할 수 있습니다. 제네릭 메서드를 사용하면 다양한 데이터 타입에 대해 동일한 코드를 사용할 수 있습니다. 또한, 제네릭을 사용하여 반환 값을 타입 안정성 있게 처리할 수 있습니다.

    public <T> void printArray(T[] arr) {
        for (T element : arr) {
            System.out.println(element);
        }
    }
    
    String[] strArray = {"hello", "world"};
    printArray(strArray);
    
    Integer[] intArray = {1, 2, 3};
    printArray(intArray);

    위 예제에서는 제네릭 메서드인 printArray를 정의하고 있습니다. 메서드 내부에서는 T 타입을 사용하여 배열의 요소를 출력합니다. 이렇게 정의된 제네릭 메서드는 String[], Integer[] 등 다양한 데이터 타입에 대해 동일한 코드를 사용할 수 있습니다.

     

    클래스(Class)

    제네릭을 사용할 때는 클래스나 메서드에서 사용할 데이터 타입을 선언할 때, 타입 매개변수(type parameter)를 사용합니다. 이렇게 선언된 타입 매개변수는 클래스나 메서드를 사용할 때 구체적인 타입으로 대체됩니다.

    public class Box<T> {
        private T data;
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }

    위 예제에서는 제네릭 클래스인 Box를 정의하고 있습니다. 클래스 이름 뒤에 <T>와 같이 타입 매개변수를 선언하고, 클래스 내부에서는 T를 데이터 타입으로 사용합니다. 이렇게 정의된 Box 클래스를 사용하면 다양한 데이터 타입을 저장할 수 있습니다.

    Box<String> stringBox = new Box<>();
    stringBox.setData("hello");
    
    Box<Integer> intBox = new Box<>();
    intBox.setData(123);

    위 예제에서는 Box 클래스를 사용하여 String과 Integer 데이터를 저장하고 있습니다. Box<String>과 Box<Integer>와 같이 구체적인 데이터 타입을 명시하여 사용하며, 제네릭을 사용함으로써 타입 안정성을 보장할 수 있습니다.

     

     

    이넘(Enum)

    Enum(열거형)은 서로 연관된 상수들의 집합을 정의하는 기능입니다. Enum을 사용하면 코드를 더 간결하고 명확하게 작성할 수 있습니다. Enum은 클래스로 정의되며, 다음과 같이 정의할 수 있습니다.

    public enum Direction {
        EAST,
        WEST,
        SOUTH,
        NORTH
    }

    위 예제에서는 Direction Enum을 정의하고 있습니다. Enum 내부에는 EAST, WEST, SOUTH, NORTH와 같이 상수가 정의되어 있습니다. 이렇게 정의된 Enum은 다음과 같이 사용할 수 있습니다.

    Direction east = Direction.EAST;
    if (east == Direction.EAST) {
        System.out.println("East");
    }

    위 예제에서는 Direction Enum을 사용하여 EAST와 같은 상수를 정의하고 있습니다. Direction.EAST와 같이 상수를 참조할 수 있으며, 조건문에서도 Enum을 사용하여 비교할 수 있습니다. 또한, Enum은 메서드와 필드를 정의할 수 있습니다. 다음과 같이 Enum에 메서드를 추가하여 사용할 수 있습니다.

    public enum Direction {
        EAST("right"),
        WEST("left"),
        SOUTH("down"),
        NORTH("up");
    
        private final String description;
    
        Direction(String description) {
            this.description = description;
        }
    
        public String getDescription() {
            return description;
        }
    }

    위 예제에서는 Direction Enum에 getDescription() 메서드를 추가하여, 각 상수의 설명(description)을 반환하도록 하였습니다. 이렇게 정의된 Enum은 다음과 같이 사용할 수 있습니다.

    Direction east = Direction.EAST;
    System.out.println(east.getDescription());

    Enum은 코드를 더 간결하고 명확하게 작성할 수 있도록 도와주는 기능입니다. Enum을 사용하면 상수를 보다 효과적으로 관리하고, 코드의 가독성과 유지보수성을 높일 수 있습니다.

     

     

    어노테이션(Annotation)

    Java에서 제공하는 어노테이션 중에서도 자주 사용되는 @Override, @Deprecated, @SuppressWarnings 등의 예제를 통해 어노테이션을 설명해보겠습니다.

     

    @Override

    @override 어노테이션은 메서드를 재정의할 때 사용합니다. @override 어노테이션을 사용하면 컴파일러가 해당 메서드가 상위 클래스나 인터페이스의 메서드를 재정의하는 것인지 확인할 수 있습니다.

    public class Parent {
        public void print() {
            System.out.println("Parent");
        }
    }
    
    public class Child extends Parent {
        @Override
        public void print() {
            System.out.println("Child");
        }
    }

    위 예제에서는 Parent 클래스와 Child 클래스를 정의하고 있습니다. Child 클래스에서는 @override 어노테이션을 사용하여 Parent 클래스의 print() 메서드를 재정의하고 있습니다.

     

    @Deprecated

    @Deprecated 어노테이션은 사용이 권장되지 않는 메서드나 클래스를 나타냅니다. @Deprecated 어노테이션을 사용하면, 다른 개발자들이 해당 메서드나 클래스를 사용할 때 경고를 받을 수 있습니다.

    public class MyClass {
        @Deprecated
        public void oldMethod() {
            //...
        }
    }

    위 예제에서는 MyClass 클래스의 oldMethod() 메서드를 @Deprecated 어노테이션으로 표시하고 있습니다.

     

    @SuppressWarnings

    @SuppressWarnings 어노테이션은 컴파일러의 경고를 무시하도록 지정합니다. 이 어노테이션을 사용하면, 컴파일러에서 발생하는 경고 메시지를 무시할 수 있습니다.

    @SuppressWarnings("unchecked")
    public void someMethod() {
        List list = new ArrayList();
        list.add("hello");
    }

    위 예제에서는 someMethod() 메서드에서 List를 사용하고 있습니다. 그러나 List를 제네릭으로 지정하지 않았기 때문에, 컴파일러에서는 경고를 발생시킵니다. 이 경우에 @SuppressWarnings 어노테이션을 사용하여 경고를 무시할 수 있습니다.

    어노테이션은 코드의 가독성과 유지보수성을 높이는 데에 매우 유용한 기능입니다. @Override, @Deprecated, @SuppressWarnings 등의 어노테이션은 자주 사용되며, 다른 어노테이션도 사용자가 직접 정의하여 사용할 수 있습니다.

     

    직접 정의한 @Required 어노테이션

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Required {
    }

    위 예제에서는 @Required 어노테이션을 정의하고 있습니다. 이 어노테이션은 필드에 적용되며, 해당 필드가 반드시 입력되어야 함을 나타냅니다.

    public class User {
        @Required
        private String name;
    
        @Required
        private String email;
    
        private String address;
        
        // getters and setters
    }

    위 예제에서는 User 클래스의 필드에 @Required 어노테이션을 적용하고 있습니다. 이 어노테이션을 사용하면, 해당 필드가 반드시 입력되어야 하는 필수값임을 알 수 있습니다.

    import java.lang.reflect.Field;
    
    public class Validator {
        public static void validate(Object obj) throws IllegalAccessException {
            for (Field field : obj.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Required.class)) {
                    field.setAccessible(true);
                    if (field.get(obj) == null) {
                        throw new IllegalArgumentException("Field " + field.getName() + " is required.");
                    }
                }
            }
        }
    }

    위 예제에서는 Validator 클래스에서 User 객체의 필드를 검증하는 예제를 보여줍니다. User 클래스의 필드 중에서 @Required 어노테이션이 적용된 필드가 있는 경우, 해당 필드의 값이 null인지 확인하고 필수값임에도 입력되지 않은 경우 IllegalArgumentException을 발생시킵니다.

    어노테이션은 개발자가 직접 정의하여 사용할 수 있기 때문에, 다양한 용도로 활용됩니다. 이 예제에서는 @Required 어노테이션을 사용하여 필드의 필수값 여부를 나타내었지만, 더 다양한 용도로 사용될 수 있습니다.

     

Designed by Tistory.