제네릭
프로그래밍을 쉽게 작성하기 위해 다양한 종류의 데이터를 처리할 수 있는 클래스와 메소드를 작성하는 기법
코드가 중복되지 않음(재사용성) => 마치 상속처럼
*클래스로 이용됨.
*객체를 만들 수 없다 => 클래스를 만드는 모양 틀임.
동작 방식
자료형(타입)을 구체적으로 명시X => T와 같은 기호로 적음.
그 후, 객체를 생성할 때 T자리에 구체적인 자료형을 적음. => 자료형을 클래스의 매개변수로 만든다는 뜻
+) 제네릭 이전에는 어떻게 동작했을까
다형성을 통해 제네릭 기법 전에도 모든 종류의 객체를 받을 수 있는 클래스 작성이 가능했음.
객체를 Object 타입으로 받으면 됨. Object클래스는 최상위 클래스이므로, 업캐스팅으로 모든 종류의 객체를 받을 수 있음
*모든 객체는 Object 클래스의 자손임.
//Object 클래스는 모든 클래스의 상위 클래스임.
class Box {
private Object data;
public void set(Object data) { this.data = data; }
public Object get() { return data; }
}
public class Test {
public static void main(String[] args) {
Box b = new Box(); //Box 클래스 객체 생성
b.set("Hello World!"); //업캐스팅 발생 -> 문자열 객체 저장
String s = (String)b.get(); //다운캐스팅 발생 -> Object 타입을 String 타입으로 형변환
b.set(10); //업캐스팅 발생 -> 정수 객체 저장
Integer i = (Integer) b.get(); //다운캐스팅 발생 -> Object 타입을 Integer 타입으로 형변환
}
}
문제점
1. 데이터를 갖고 올 때 항상 형변환이 필요함
2. 문자열을 저장하고 다른 타입 객체로 형변환 할 수 있음 -> 심지어 이클립스에서 빨간 오류가 안 뜨고, 실행해야 컴파일 오류가 남. 즉, 오류 찾기가 어려움.
b.set("Hello World!");
Integer i = (Integer)b.get(); //오류
이러한 문제점을 해결하기 위해 재네릭 기법 등장
제네릭 이용 방법
타입 매개 변수 : 타입을 변수로 표시한 것 => 프로그래머가 결정함
*아래 코드의 타입 매개 변수는 T임.
class Box<T> {
private T data;
public void set(T data) { this.data = data; }
public T get() { return data; }
}
public class Test {
public static void main(String[] args) {
Box<String> b1 = new Box<String>(); //타입 매개 변수 값을 T가 아닌 String으로
b1.set("Hello World"); //이미 b는 String 객체이므로, 업캐스팅X
String s = b1.get(); //형변환 또한 할 필요X
//int는 클래스가 아닌 기초 자료형이므로 사용할 수 없음. 객체 자료형이 와야 함.
Box<Integer> b2 = new Box<Integer>();
b2.set("Hello World!"); //b2는 이미 Integer 객체이므로, 문자열을 저장하려고 하면 컴파일 오류 -> 오류 감지 편리
}
}
생성자 호출 시, 타입 인수를 구체적으로 주지 않아도 됨. 컴파일러가 추측함. 그러나 써 주는 것이 가독성이 더 좋음.
Box<String> b1 = new Box<>(); //가능
Box<Integer> b2 - new Box<>(); //가능
제네릭 메소드
메소드도 제네릭으로 가능.
class MyArrayAlg {
public static <T> T getLast(T[] a) {
return a[a.length - 1];
}
}
public class Test {
public static void main(String[] args) {
String[] language = { "C++", "C#", "JAVA" };
String last = MyArrayAlg.getLast(language);
System.out.println(last);
}
}
MyArrayAlg 클래스 설명
public static : 정적 메소드임을 나타냄. main()에서 MyArrayAlg.(메소드이름)으로 접근 가능함.
<T> : 타입 피라미터의 값(타입 매개변수)
T : 반환 하는 값의 타입
getLast : 메소드 명
T[] a : T[] 배열 a
* getLast메소드는 일반 클래스 안에 정의되어 있지만 <T>를 갖고 있으므로 제네릭 메소드임.
* 타입 매개 변수
- 이 부분이 빠지면 컴파일 오류 발생. JAVA에서 대문자 T라는 이름을 가진 클래스 이름이 없다는 의미로 오류가 남.
- 위치 : 반환값 앞
- 변수명 : 사용자 마음이지만 일반적으로 사용하는 이름이 있음
E : Element (요소)
K : Key
N : Number
T, S, U, V ... : 첫 번째 Type, 두 번째 Type, 세 번째 Type, 네 번째 Type
V : Value
- 객체화가 될 수 없음
f1 <String, int> 불가
f2 <String, Integer> 가능
* main에서 제네릭 메소드를 호출 할 때 아래의 코드가 문법적으로 옳은 코드임.
String last = MyArrayAlg.<String>getLast(language);
getLast는 T자리에 의미 있는 값이 들어가야 함. T자리에 String이 들어감을 명시해 주는 것임.
하지만 <> 부분이 없어도 컴파일 오류는 안 나고, 잘 작동함. JAVA에서 타입 유추를 하기 때문(type in inference)
* 반드시 제네릭 메소드가 static일 필요는 없음. -> static이 없다면, 객체를 생성해야함.
MyArrayAlg arr = new MyArrayAlg();
String last = arr.<String>getLast(language);
+) 피라미터 : 입력변수, 매개변수 -> 메소드의 입력 값을 저장하는 변수
- 단일 피라미터 public static void f1 (TYPE X) { }
- 다중 피라미터 public static void f2 (TYPE X, TYPE Y) { }
public class Test {
public static <T> void printArray(T[] array) {
for (T element : array) {
String s = String.format("%s ", element);
System.out.printf(s);
}
System.out.println();
}
public static void main(String[] args) {
Integer[] iArray = { 10, 20, 30, 40, 50 };
Double[] dArray = { 1.1, 1.2, 1.3, 1.4, 1.5 };
Character[] cArray = { 'K', 'O', 'R', 'E', 'A' };
printArray(iArray);
printArray(dArray);
printArray(cArray);
}
}
//출력 결과
10 20 30 40 50
1.1 1.2 1.3 1.4 1.5
K O R E A
'JAVA' 카테고리의 다른 글
상속과 구성 - 객지설 12주차 (0) | 2024.05.27 |
---|---|
컬렉션/ArrayList/HashSet - 객지설 11주차 (0) | 2024.05.23 |
예외 처리 - 객지설 7~9주차 (0) | 2024.05.10 |
인터페이스 - 6주차 (0) | 2024.05.05 |
다형성 - 객지설 5주차 (0) | 2024.05.04 |