-

제네릭(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 어노테이션을 사용하여 필드의 필수값 여부를 나타내었지만, 더 다양한 용도로 사용될 수 있습니다.
'Software engineer > Java' 카테고리의 다른 글
[Java] #7 스레드(Thread) (2) 2023.05.02 [Java] #6 람다(Lambda), 스트림(Stream) (1) 2023.05.02 [Java] #4 배열(Array), 컬렉션(Collection), 맵(Map) (3) 2023.04.30 [Java] #3 제어문(Control statements) (2) 2023.04.28 [Java] #2 연산자(Operator) (0) 2023.04.28