Generic이란
직역하면 ‘일반적인’이란 뜻이다. 즉 데이터 형식에 의존하지않고 하나의 값이 여러개의 타입을 가질수 있도록 하는 방법이다
Generic의 장점
•
잘못된 타입이 들어올 수 있는것을 컴파일 단계에서 방지할 수 있다
•
클래스 외부에서 타입을 지정해주기때문에 관리하기가 편하다
•
코드의 재사용성이 높아진다
선언과 사용방법
class CName <T> {
...
}
Interface IName <T> {
...
}
CName<데이터 타입> name = new CName<>();
Java
복사
기본적으로 < >를 하고 그 안에 타입을 넣는다.
여기서 T는 암묵적으로 정한 타입명명규칙이다
타입 | 설명 |
<T> | Type |
<E> | Element |
<K> | Key |
<V> | Value |
<N> | Number |
예시
Class CName<E> {
private E element;
void set(E element) {
this.element = element;
}
E get() {
return element;
}
}
public class Main {
public static void main(String[] args) {
CName<String> a = new CName<String>();
CName<Integer> b = new CName<Integer>();
a.set("10");
b.set(10);
System.out.println("a data : " + a.get());
System.out.println("b data : " + b.get());
}
}
Java
복사
위처럼 타입 지정을 객체선언때 지정해주고 타입을 String, Integer로 다르게 주었다
그리고 결과를 보면 둘다 정상적으로 출력이 된다
여기서 드는 궁금증은 왜 int, char… 가 아닌 Integer인가다
이유는 제네릭에는 참조타입밖에 오지 못한다.
또한 제네릭은 2개를 선언할수있다
Class CName<K,V> {
private K first;
private V second
void set(K first, V second) {
this.first = first;
this.second = second;
}
K getF() {
return first;
}
V getS() {
return second;
}
}
public class Main {
public static void main(String[] args) {
CName<String, Integer> gTest = new CName<String, Integer>();
gTset.set("10",10);
System.out.println(gTest.getF());
System.out.println(gTest.getS());
}
}
Java
복사
위의 코드와 같은 값인 String, Integer로 선언된 10, 10이 나오게 된다
제네릭 메소드
제네릭은 메소드에서도 쓸수가 있다
public <T> T genericMethod(T t) {
...
}
[접근제어자] <제네릭타입> [리턴타입] [메소드명]([제네릭타입] [파라미터]) {
...
}
Java
복사
제네릭 메소드는 파라미터의 타입에 따라 T가 결정된다
그리고 제네릭 클래스에서 스태틱(static)은 사용이 불가능하다
이유는 스태틱이 붙을경우 프로그램 실행시 바로 메모리에 올라가는데 제네릭은 타입을 지정해주지 않았기때문에 사용할수가 없다.
class ClassName<E> {
static E genericMethod(E o) {
return o;
}
}
class Main {
public static void main(String[] args) {
// ClassName 객체가 생성되기 전에 접근할 수 있으나 타입을 지정할 방법이 없어 에러남
ClassName.getnerMethod(3);
}
}
Java
복사
타입을 지정하지 못하기때문에 위의 코드는 에러가 난다
class ClassName<E> {
private E element; // 제네릭 타입 변수
void set(E element) { // 제네릭 파라미터 메소드
this.element = element;
}
E get() { // 제네릭 타입 반환 메소드
return element;
}
// 아래 메소드의 E타입은 제네릭 클래스의 E타입과 다른 독립적인 타입이다.
static <E> E genericMethod1(E o) { // 제네릭 메소드
return o;
}
static <T> T genericMethod2(T o) { // 제네릭 메소드
return o;
}
}
public class StaticGeneric1 {
public static void main(String[] args) {
ClassName<String> a = new ClassName<String>();
ClassName<Integer> b = new ClassName<Integer>();
a.set("10");
b.set(10);
System.out.println("a data : " + a.get());
System.out.println("b data : " + b.get());
System.out.println();
//제네릭 메소드
System.out.println("<E> returnType : " + ClassName.genericMethod1(3));
System.out.println("<E> returnType : " + ClassName.genericMethod1("ABCD"));
System.out.println("<T> returnType : " + ClassName.genericMethod1(a));
System.out.println("<T> returnType : " + ClassName.genericMethod1(3.0));
}
}
Java
복사
하지만 위의 코드는 가능하다
제네릭 메소드는 제네릭 클래스와 별개로 독립적인 선언이 가능하다
그래서 제네릭클래스의 <E>와 제네릭메소드의<E>는 다른 타입이다
또한 제네릭메소드는 호출시에 타입을 정해주기때문에 스태틱으로 선언이 가능하다
제한된 Generic, 와일드카드(extends, super, ‘?’)
•
extends : extends 뒤에 선언한 타입 밑에 타입들만 선언할 수 있다
•
super : super 뒤에 선언한 타입 위의 타입들만 선언할 수 있따
•
와일드카드 : <?>로 선언하고 알수없는 타입이다
<K extends T> //T와 T의 하위타입만 가능 (K는 들어오는 타입으로 지정됨)
<K super T> //T와 T의 상위타입만 가능 (K는 들어오는 타입으로 지정됨)
<? extends T> //T와 T의 하위타입만 가능
<? super T> //T와 T의 상위타입만 가능
<?> //모든 타입 가능 <? extends Object>랑 같은 의미
Java
복사
와일드카드
쉽게 말해서 알수없는타입(unknown type)이다
이 ?에는 내가 만든클래스타입, Integer, String 등등 범위가 정말 무제한이다
그래서 extends나 super로 상한선 하한선을 정해주어야한다