컴파일시 타입을 체크해 주는 기능
ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
tvList.add(new Audio()); // 컴파일 에러. Tv 외에 다른 타입은 저장 불가
객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줌
ArrayList tvLIst = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv)tvList.get(0);
ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
Tv t = tvList.get(0); // 형변환 불필요
타입체크와 형변환 생략이 가능해져 코드가 간결해진다.
클래스를 작성할 때, Object타입 대신 타입 변수(E)를 선언해서 사용
일반클래스 -> 지네릭클래스
public class ArrayList extends AbstractList {
private transient Object[] elementData;
public boolean add(Object o) {}
public Object get(int index) {}
...
}
public class ArrayList<E> extends AbstractList<E> {
private transient E[] elementData;
public boolean add(E o) {}
public E get(int index) {}
...
}
객체를 생성시, 타입 변수(E) 대신 실제 타입을 지정(대입)
ArryList<Tv> tvList = new ArrayList<Tv>();
// 위의 ArrayList의 E부분에 Tv가 들어간다.
타입 변수 대신 실제 타입이 지정되면, 형변환 생략 가능
지네릭 클래스. 'T의 Box' 또는 'T Box'라고 읽는다.
Box<T>
타입 변수 또는 타입 매개변수.(T는 타입 문자)
T
원시 타입(raw type)
Box
참조 변수와 생성자의 대입된 타입은 일치해야 한다.
ArrayList<Tv> list = new ArrayList<Tv>();
ArrayList<Product> list = new ArrayList<Tv>(); // 에러
지네릭 클래스간의 다형성은 성립.(여전히 대입된 타입은 일치해야 함)
List<Tv> list = new ArrayList<Tv>();
List<Tv> list = new LinkedList<Tv>();
매개변수의 다형성도 성립
ArrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv()); // Product의 자손
list.add(new Audio()); // Product의 자손
사용 예
package ch02;
import java.util.*;
import static java.util.Collections.*;
class Product {}
class Tv extends Product {}
class Audio extends Product {}
public class EX3 {
public static void main(String[] args) {
ArrayList<Product> productList = new ArrayList<Product>();
ArrayList<Tv> tvList = new ArrayList<Tv>();
productList.add(new Tv());
productList.add(new Audio());
tvList.add(new Tv());
tvList.add(new Tv());
printAll(productList);
}
public static void printAll(ArrayList<Product> list) {
for (Product p : list) {
System.out.println(p);
}
}
}
클래스를 작성할 때, Object 타입 대신 T와 같은 타입 변수를 사용
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
Iterator it = list.iterator();
while(it.hasNext()) {
Student s = (Student)it.next();
...
}
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
Iterator<Student> it = list.iterator();
while(it.hasNext()) {
Student s = it.next();
...
}
예시
package ch02;
import java.util.*;
import static java.util.Collections.*;
public class EX3 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("A", 1, 1));
list.add(new Student("B", 1, 2));
list.add(new Student("C", 2, 1));
Iterator<Student> it = list.iterator();
while(it.hasNext()) {
Student s = it.next();
System.out.println(s.name);
}
}
}
class Student {
String name = "";
int ban;
int no;
Student(String name, int ban, int no){
this.name = name;
this.ban = ban;
this.no = no;
}
}
A
B
C
여러 개의 타입 변수가 필요한 경우, 콤마를 구분자로 선언
package ch02;
import java.util.*;
import static java.util.Collections.*;
public class EX3 {
public static void main(String[] args) {
HashMap<String, Student> map = new HashMap<>();
map.put("A", new Student("a", 1, 1, 100, 100, 100));
Student s = map.get("A");
System.out.println(s.name);
}
}
class Student {
String name = "";
int ban;
int no;
int kor;
int eng;
int math;
Student(String name, int ban, int no, int kor, int eng, int math){
this.name = name;
this.ban = ban;
this.no = no;
this.kor = kor;
this.eng = eng;
this.math = math;
}
}
a
extends로 대입할 수 있는 타입을 제한
class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정가능
ArrayList<T> list = new ArrayList<T>();
...
}
FruitBox<Apple> appleBox = new FruitBox<Apple>();
인터페이스인 경우에도 extends를 사용
interface Eatable {}
class FruitBox<T extends Eatable> {...}
예시
package ch02;
import java.util.*;
import static java.util.Collections.*;
public class EX3 {
public static void main(String[] args) {
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grape> grapeBox = new FruitBox<Grape>();
fruitBox.add(new Fruit());
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple());
System.out.println(fruitBox);
System.out.println(appleBox);
}
}
interface Eatable {}
class Fruit implements Eatable {
public String toString() {return "Fruit";}
}
class Apple extends Fruit { public String toString() { return "Apple"; }}
class Grape extends Fruit { public String toString() { return "Grape"; }}
class Toy { public String toString() { return "Toy"; }}
class FruitBox<T extends Fruit & Eatable> extends Box<T> {}
class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
T get(int i) { return list.get(i); }
int size() { return list.size(); }
public String toString() { return list.toString(); }
}
타입 변수에 대입은 인스턴스 별로 다르게 가능
Box<Apple> appleBox = new Box<Apple>();
Box<Grape> grapeBox = new Box<Grape>();
static멤버에 타입 변수 사용 불가
모든 인스턴스가 공통으로 사용하기 때문
class Box<T> {
static T item; // 에러
}
배열 생성할 때 타입 변수 사용불가. 타입 변수로 배열 선언은 가능
class Box<T> {
T[] itemArr;
...
T[] toArray() {
T[] tmpArr = new T[itemArr.length]; // 에러
}
}