[Generic (제네릭)]
클래스 내부에서 사용할 데이터 타입을 인스턴스 생성시 확정하는 것을 제네릭이라 한다.
제네릭은 다양한 타입의 객체를 다루는 메소드 및 컬렉션 클래스를 컴파일 시, 타입 체크를 해주는 기능을 한다.
객체 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄인다.
※ ArrayList 같은 컬렉션 클래스는 다양한 종류의 객체를 담을 수 있지만, 보통 아래와 같이 한 종류의 객체를 담는 경우가 더 많다. 또한 꺼낼 때 마다 타입체크를 하고 형변환 하는 것은 아무래도 불편할 수 밖에 없는데, 제네릭이 이와 같은 불편함 들을 해소해준다.
List<Person> list = new ArrayList<>();
list.add(new Person("영심이"));
list.add(new Person("홍길동"));
list.get(0);
1. 제네릭 타입
타입을 파라미터(<T>)로 가지는 클래스 혹은 인터페이스
타입 파라미터(<T>) 를 클래스 혹은 인터페이스 명 뒤에 두어 선언
public class Sample<T>{}
public interface Sample<T>{}
[Print.class (제네릭 타입)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package Java.Generic;
public class Print<T> {
private T text;
public void setText(T text) {
this.text = text;
}
public void printText() {
System.out.println(text);
}
}
|
cs |
[Main.class]
p.text 는 String,
b.text 는 Integer(int) 데이터 타입을 갖게된다.
: 제네릭을 사용하여 클래스를 정의할 때 데이터 타입을 정하지 않고, 인스턴스를 생성할 때 타입을 저장
※ jdk 1.7 버전 이상은 실제 타입 생략 가능
Print<Integer> p = new Print<>(); // Print<Integer> p = new Print<Integer>(); 와 같음
※ Raw Type : 타입 매개변수가 없는 제네릭 클래스
generic은 jdk 1.5 버전에 지원됐으며, 1.5 버전 이전의 소스와 문제가 생기는걸 방지하기 위해 Raw Type 을 허용
아래와 같이 데이터타입을 주지 않고 선언 할 경우 데이터 타입(타입 매개변수)은 Object로 간주한다.
List list = new ArrayList<>(); //List<Object> list = new ArrayList<>(); 와 같음
[제네릭의 장점]
1. 타입안정성 제공 :
명시한 타입의 데이터가 아닌 다른 형태의 데이터 타입으로의 형변환을 방지 (위 Main.class 의 8번, 14번 참고)
2. 타입체크와 형변환 생략 가능 :
다룰 객체의 데이터 타입을 미리 명시
[다중 타입매개변수]
타입매개변수가 N개인 경우, 아래와 같이 사용 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package Java.Generic;
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value){
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
|
cs |
Client(호출부)
Pair<String, Integer> pair1 = new Pair<>("key1", 1);
System.out.println(pair1.getKey()); //key1
System.out.println(pair1.getValue()); //1
Pair<Integer, Integer> pair2 = new Pair<>(1, 2);
System.out.println(pair2.getKey()); //1
System.out.println(pair2.getValue()); //2
[제네릭과 다형성]
public class Print{}
public class ChilePrint extends Print{}
위처럼 Print 클래스, 이를 상속하는 ChildPrint 클래스가 있을 때
아래와 같이 사용 가능.
1
2
3
4
5
6
7
8
9
10
|
public class Client {
public static void main(String[] args) {
List<Print> list = new ArrayList<>();
list.add(new Print());
list.add(new ChildPrint());
Print p = list.get(0);
ChildPrint cp = (ChildPrint)list.get(0);
}
}
|
cs |
Print 를 상속하는 ChildPrint 는 Print 타입파라미터를 갖는 list 인스턴스에 add 할 수 있다.
2. 제네릭 메소드
리턴 타입 및 매개변수 타입을 타입 매개변수로 갖는 메소드
일반 클래스의 메소드에서도 타입 매개변수를 사용하여 제네릭 메소드 정의 가능
리턴타입 앞에 타입 매개변수(<>)를 추가한 후, 리턴타입과 매개타입을 타입 매개변수로 사용가능
[Cart.class]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package Java.Generic;
import java.util.ArrayList;
import java.util.List;
public class Cart<T> {
private List<T> items;
public Cart() {
if(items == null)items = new ArrayList<>();
}
public void setItem(T item) {
this.items.add(item);
}
public List<T> getItems() {
return items;
}
}
|
cs |
[Calculator.class (제네릭 메소드)]
1
2
3
4
5
6
7
8
9
10
11
12
|
package Java.Generic;
public class Calculator {
public static <T> int add(Cart<T> cart1, Cart<T> cart2) {
int result = 0;
result = cart1.getItems().size() + cart2.getItems().size();
return result;
}
}
|
cs |
[Client.class (호출부)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package Java.Generic;
public class Client {
public static void main(String[] args) {
Cart<String> cart1 = new Cart<>();
cart1.setItem("초콜렛");
cart1.setItem("슬리퍼");
Cart<String> cart2 = new Cart<>();
cart2.setItem("생수");
int totalSize = Calculator.add(cart1, cart2);
System.out.println(totalSize); // 3
}
}
|
cs |
3. 타입 파라미터의 제한
상속 및 구현 관계를 이용하여 타입 제한
※ 상위 타입이 interface인 경우에도 extends 키워드 사용.
public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) {...}
[Product.class]
최상위 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package Java.Generic;
public class Product {
private String product;
public void setProduct(String product) {
this.product = product;
}
public String getProduct() {
return this.product;
}
}
|
cs |
[Food.class]
Product를 상속받는 하위 클래스
1
2
3
4
|
package Java.Generic;
public class Food extends Product{}
|
cs |
[Fruit.class]
Food 를 상속받는 하위 클래스
1
2
3
4
|
package Java.Generic;
public class Fruit extends Food{}
|
cs |
[Cart.class]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package Java.Generic;
import java.util.ArrayList;
import java.util.List;
public class Cart<T> {
private List<T> items;
public Cart() {
if(items == null)items = new ArrayList<>();
}
public void setItem(T item) {
this.items.add(item);
}
public List<T> getItems() {
return items;
}
}
|
cs |
[Calculator.class (제네릭 메소드)]
타입 파라미터를 Food 로 제한한다. (Food 혹은 Foor 하위 클래스만 타입 파라미터로 받을 수 있다)
1
2
3
4
5
6
7
8
9
10
11
12
|
package Java.Generic;
public class Calculator {
public static <T extends Food> int add(Cart<T> cart1, Cart<T> cart2) {
int result = 0;
result = cart1.getItems().size() + cart2.getItems().size();
return result;
}
}
|
cs |
[Client.class (호출부)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package Java.Generic;
public class Client {
public static void main(String[] args) {
Cart<Food> cart1 = new Cart<>();
Product p = new Food();
p.setProduct("초콜릿");
cart1.setItem((Food)p);
Cart<Food> cart2 = new Cart<>();
Product p2 = new Food();
p2.setProduct("과자");
cart2.setItem((Food)p2);
int totalSize = Calculator.add(cart1, cart2);
System.out.println(totalSize);
}
}
|
cs |
※ 해당 메소드 호출 시, 타입 파라미터를 섞어서 사용 할 수 없다.
: 16번 라인의 .add(...) 호출 시, 넘겨주는 매개변수 cart1 의 타입 파라미터(6번 라인의 Cart<Food>)에 의해 .add(...) 메소드의 타입파라미터(Calculator.class의 4번 라인의 <T>)가 정해진다.
예를 들어, 아래와 같은 경우
14번 라인에서 빨간줄이 그어진다.
cart3 의 Fruit 으로 .add(...) 메소드의 타입파라미터가 정해지지만, Food 를 담고있는 cart4 를 매개변수로 넣으려고 하고 있으므로 not applicable for the arguments 에러가 발생한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package Java.Generic;
public class Client {
public static void main(String[] args) {
Cart<Food> cart1 = new Cart<>();
Calculator.add(cart1, cart1);
Cart<Fruit> cart2 = new Cart<>();
Calculator.add(cart2, cart2);
Cart<Fruit> cart3 = new Cart<>();
Cart<Food> cart4 = new Cart<>();
Calculator.add(cart3, cart4); //compile error
}
}
|
cs |
4. 와일드카드
1. 제네릭 타입<?>: Unbounded Wildcards(제한 없음)
타입 파라미터로 모든 클래스나 인터페이스 타입이 올 수 있다.
2. 제네릭 타입<? extends 상위 타입>: Upper Bounded Wildcards(상위 클래스 제한)
타입 파라미터로 상위 타입의 하위 타입만 올 수 있다.
3. 제네릭 타입<? super 하위 타입>: Lower Bounded Wildcards(하위 클래스 제한)
타입 파라미터로 하위 타입의 상위타입만 올 수 있다.
[example]
public class Product {}
public class Food extends Product {}
public class Fruit extends Food {}
[Calculator.class : 타입 파라미터 제한과 와일드카드 사용의 차이]
add1(...) : 타입 파라미터를 제한
add3(...) : 와일드카드를 사용하여 매개변수의 타입을 제한
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package Java.Generic;
public class Calculator {
public static <T extends Food> int add1(Cart<T> cart1, Cart<T> cart2, Cart<T> cart3) {
//제네릭 메소드에서의 타입 파라미터의 제한
//타입파라미터를 Food 로 제한
//매개변수는 타입파라미터인 T를 갖는 Cart이며, T는 Food 및 Food 의 subclass 로 제한
int result = 0;
result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
return result;
}
// public static <T super Food> int add2(Cart<T> cart1, Cart<T> cart2, Cart<T> cart3) {
// //제네릭 메소드에서 타입 파라미터를 제한할 때 위와 같은 super키워드는 불가
// //compile error
// int result = 0;
// result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
// return result;
// }
public static int add3(Cart<? super Food> cart1, Cart<?> cart2, Cart<? extends Food> cart3) {
//매개변수의 타입을 와일드카드 (?)를 사용하여 제한
//cart1 맥대변수(Cart)의 타입파라미터 : Food 및 Food 를 subclass
//cart2 매개변수(Cart)의 타입파라미터 : 아무 타입이나 담고있는 Cart
//cart3 매개변수(Cart)의 타입파라미터 : Food 및 Food 의 subclass
int result = 0;
result = cart1.getItems().size() + cart2.getItems().size() + cart3.getItems().size();
return result;
}
}
|
cs |
[Client.class]
1. add1사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package Java.Generic;
public class Client {
public static void main(String[] args) {
Cart<Food> cart1 = new Cart<>();
Calculator.add1(cart1, cart1, cart1); //타입파라미터를 Food 로 지정
Cart<Fruit> cart2 = new Cart<>();
Calculator.add1(cart2, cart2, cart2); //타입파라미터를 Fruit 으로 지정
Cart<Fruit> cart3 = new Cart<>();
Cart<Food> cart4 = new Cart<>();
Calculator.add1(cart3, cart3, cart4); //car3 매개변수에 의해 Fruit 으로 타입파라미터가 정해지지만, cart4 Food를 넣으려하기 때문에 Compile Error
}
}
|
cs |
2. add3 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package Java.Generic;
public class Client {
public static void main(String[] args) {
Cart<Product> cart1 = new Cart<>();
Product p = new Product();
p.setProduct("바가지");
cart1.setItem(p);
Cart<String> cart2 = new Cart<>();
cart2.setItem("연필");
Cart<Fruit> cart3 = new Cart<>();
Product p2 = new Fruit();
p2.setProduct("사과");
Product p3 = new Fruit();
p3.setProduct("수박");
cart3.setItem((Fruit)p2);
cart3.setItem((Fruit)p3);
int totalSize = Calculator.add3(cart1, cart2, cart3);
System.out.println(totalSize);
}
}
|
cs |
※
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
참고:
https://devbox.tistory.com/entry/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD
https://movefast.tistory.com/74
https://ict-nroo.tistory.com/42
'back > java' 카테고리의 다른 글
[Java] lambda 람다 2 (메소드참조) (0) | 2020.01.17 |
---|---|
[Java] lambda 람다 1 (기본문법) (0) | 2020.01.15 |
[Java] Compile (0) | 2019.12.22 |
[Java] Collection Framework (0) | 2019.12.21 |
Comparator, Comparable + Arrays.sort() 그리고 인터페이스.. (0) | 2019.09.02 |