Why Use Generics? 😄
간단히 말해서, 제네릭은 클래스, 인터페이스 및 메서드를 정의할 때 타입(클래스 및 인터페이스)을 파라미터로 사용할 수 있도록 합니다. 제네릭을 통해 동일한 코드를 다양한 타입으로 재사용할 수 있게 되며, 타입 안전성을 강화하고 코드의 가독성을 높일 수 있습니다.
제네릭의 주요 이점 💡
1. 컴파일 시간에 더 강력한 타입 검사: 자바 컴파일러는 제네릭 코드에 강력한 타입 검사를 적용하여, 코드가 타입 안전을 위반하는 경우 오류를 발생시킵니다. 이는 런타임 오류를 예방할 수 있어, 코드의 안정성을 높입니다.
2. 캐스트 제거: 제네릭을 사용하면 코드에서 불필요한 캐스팅을 제거할 수 있습니다. 제네릭이 없는 코드는 다음과 같습니다:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
제네릭을 사용하면 캐스팅이 필요하지 않게 됩니다:
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // no cast
제네릭 타입 🛠️
제네릭 타입은 타입 파라미터를 사용하는 클래스나 인터페이스를 의미합니다. 예를 들어, 다음과 같은 Box 클래스가 있습니다:
public class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
여기서 T는 타입 파라미터로, 이 클래스는 모든 참조 타입을 가질 수 있습니다. 즉, 제네릭을 사용함으로써 다양한 타입에 대해 유연하게 재사용할 수 있습니다.
타입 파라미터 네이밍 규칙 📛
타입 파라미터 이름은 일반적으로 단일 대문자를 사용합니다. 이는 변수 이름과 구별하기 쉽게 하기 위해서입니다. 자주 사용되는 타입 파라미터 이름은 다음과 같습니다:
• E - Element (Java Collections Framework에서 많이 사용됨)
• K - Key
• N - Number
• T - Type
• V - Value
• S, U, V 등 - 2번째, 3번째, 4번째 타입
다이아몬드 연산자 💎
Java SE 7 이상에서는 다이아몬드 연산자 <>를 사용하여 제네릭 클래스의 생성자를 호출할 때 타입 아규먼트를 생략할 수 있습니다. 예를 들어:
Box<Integer> integerBox = new Box<>();
위 코드에서 Box<Integer>의 인스턴스를 생성할 때 뒤에 타입을 생략했는데, 이를 다이아몬드 연산자라고 부릅니다.
TV 인터페이스와 구현 📺
다음은 TV 인터페이스와 이를 구현한 SamsungTV와 LGTV 클래스의 예제입니다.
TV 인터페이스 정의
public interface TVInterface {
void turnOn();
void turnOff();
void changeChannel(int channel);
void changeVolume(int volume);
}
public class SamsungTV implements TVInterface {
private String turnon;
private String turnoff;
private int channel;
private int volume;
public SamsungTV(String turnon, String turnoff, int channel, int volume) {
this.turnon = turnon;
this.turnoff = turnoff;
this.channel = channel;
this.volume = volume;
}
@Override
public void turnOn() {
System.out.println("Samsung TV is now ON");
}
@Override
public void turnOff() {
System.out.println("Samsung TV is now OFF");
}
@Override
public void changeChannel(int channel) {
System.out.println("Samsung TV changed to channel " + channel);
}
@Override
public void changeVolume(int volume) {
System.out.println("Samsung TV changed to Volume " + volume);
}
}
LGTV 클래스 구현
public class LGTV implements TVInterface {
private String turnon1;
private String turnoff1;
private int channel1;
private int volume1;
public LGTV(String turnon1, String turnoff1, int channel1, int volume1) {
this.turnon1 = turnon1;
this.turnoff1 = turnoff1;
this.channel1 = channel1;
this.volume1 = volume1;
}
@Override
public void turnOn() {
System.out.println("LG TV is now ON");
}
@Override
public void turnOff() {
System.out.println("LG TV is now OFF");
}
@Override
public void changeChannel(int channel1) {
System.out.println("LG TV changed to channel " + channel1);
}
@Override
public void changeVolume(int volume1) {
System.out.println("LG TV changed to Volume " + volume1);
}
}
메인 클래스 예제 🎮
다음은 위에서 정의한 클래스를 사용하는 예제입니다.
public class Main {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("toString" , 123);
Pair<String, String> pair2 = new Pair<>("Hello", "World");
First<String> first = new First<>("HelloWorld");
First<Integer> first2 = new First<>(12344);
Second<Integer> second = new Second<>(123456);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());
System.out.println(pair.toString());
System.out.println(pair2.getFirst());
System.out.println(pair2.getSecond());
System.out.println(first.getFirst());
System.out.println(first.toString1());
System.out.println(first2.getFirst());
System.out.println(second.toString2());
System.out.println(second.getSecond());
}
}
'Java' 카테고리의 다른 글
와일드카드와 제네릭: 자바에서의 유연성과 타입 안전성 (0) | 2024.07.17 |
---|---|
제네릭과 객체 비교의 심화: Bounded Type Parameters와 오토박싱 이해하기 (0) | 2024.07.16 |
JavaBeans와 객체 관리: 복사, 동일성, 동등성, 그리고 리팩토링 (0) | 2024.07.14 |
리팩토링: 코드 최적화의 기술, 소수 구하기 (0) | 2024.07.10 |
자바 인터페이스의 확장: 다재다능한 API 설계를 위한 디폴트 및 정적 메서드 활용 (0) | 2024.07.09 |