일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 데이터문서포맷
- 카카오APi
- 자바
- Ajax
- Session
- Multipart
- xml mapping
- JSP
- 스프링프레임워크
- HTTP
- 자바스크립트
- XML Core
- java컴파일
- 웹프로그래밍
- java annotation
- JavaScript
- Servlet
- Database
- 데이터베이스
- 세션
- 반응형웹
- Request/Response Header
- 프로그래밍용어
- 데이터포맷
- XML DOM
- Java
- JSTL
- 공문서작성규정
- xml
- 데이터규정
- Today
- Total
KyungHwan's etc.
자바 제네릭(generic) 본문
자바에서 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다.
제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.
즉, 클래스 내부에서 사용할 데이터 타입을 나중에 인스터스를 생성할 때 확정하는 것을 제네릭이라 한다.
이렇게 컴파일 시에 미리 타입 검사를 하면 다음과 같은 장점을 가진다
클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다.
반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.
객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다. ArrayList와 같은 컬렉션 클래스는 다양한 종류의 객체를 담을 수 있긴 하지만 보통 한 종류의 객체를 담는 경우가 더 많다.
제네릭의 선언 및 생성
자바에서 제네릭은 클래스와 메소드에만 다음과 같은 방법으로 선언할수 있다.
class MyArray<T> {
T element;
void setElement(T element) { this.element = element; }
T getElement() { return element; }
}
위의 예제에서 사용된 'T'를 타입 변수(type variable)라고 하며, 임의의 참조형 타입을 의미한다.
꼭 'T'뿐만 아니라 어떠한 문자를 사용해도 상관없으며, 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있다.
타입 변수는 클래스에서뿐만 아니라 메소드의 매개변수나 반환값으로도 사용할 수 있다.
위와 같이 선언된 제네릭 클래스(generic class)를 생성할 때에는 타입 변수 자리에 사용할 실제 타입을 명시해야 합니다.
MyArray<Integer> myArr = new MyArray<Integer>();
class Person<T>{
public T info;// p1 일시 데이터 타입은 String이된다.(인스턴스 생성시 String 제네릭 생성)
// p2 일시 데이터 타입은 StringBuilder이 된다.
}
public class GenericDemo {
public static void main(String[] args) {
Person<String> p1 = new Person<String>();
Person<StringBuilder> p2 = new Person<StringBuilder>();
}
}
public T info;
클래스 Person의 필드 info의 데이터 타입은 T로 되어 있다. 그런데 T라는 데이터 타입은 존재하지 않는다. 이 값은 아래 코드의 T에서 정해진다.
class Person<T>{
위 코드의 T는 아래 코드의 <> 안에 지정된 데이터 타입에 의해서 결정된다.
Person<String> p1 = new Person<String>();
위의 코드를 나눠보자. 아래 코드는 변수 p1의 데이터 타입을 정의하고 있다.
Person<String> p1
아래 코드는 인스턴스를 생성하고 있다.
new Person<String>();
즉 클래스를 정의 할 때는 info의 데이터 타입을 확정하지 않고 인스턴스를 생성할 때 데이터 타입을 지정하는 기능의 제네릭이다.
컬렉션 클래스
컬렉션 클래스 이름 바로 뒤에 저장할 객체의 타입을 적어주면, 컬렉션에 저장할 수 있는 객체는 지정한 타입의 객체 뿐이다.
컬렉션클래스<저장할 객체의 타입> 변수명 = new 컬렉션클래스<저장할 객체의 타입>();
ArrayList<Tv> tvList = new ArrayList<Tv>();
// Tv객체만 저장할 수 있는 ArrayList를 생성
tvList.add(new Tv());
tvList.add(new Radio());// 컴파일 에러
다형성을 사용해야 하는 경우에는 부모타입을 지정함으로써 여러 종류의 객체를 저장할 수 있다.
class Product{ }
class Tv extends Product{ }
class Audio extends Product{ }
//Product 클래스의 자손객체들을 저장할 수 있는 ArrayList를 생성
ArrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv());
list.add(new Audio());
Product p = list.get(0);// 형변환이 필요없다.
Tv t = (Tv)list.get(i);// 형변환을 필요로 한다.
Product 클래스가 Tv클래스의 조상이라 할지라도 아래와 같이는 할 수는 없다.
ArrayList<Product> list = new ArrayList<Tv>();//허용안함
List<Tv> tvList = new ArrayList<Tv>();// But, 허용된다.
와일드카드
보통 제네릭에서는 단 하나의 타입을 지정하지만, 와일드 카드'?'를 사용하면 된다.
보통 제네릭에서는 단 하나의 타입을 지정하지만. 와일드 카드는 하나 이상의 타입을 지정하는 것을 가능하게 해준다.
<?> // 타입 변수에 모든 타입을 사용할 수 있음.
<? extends T> // T 타입과 T 타입을 상속받는 자손 클래스 타입만을 사용할 수 있음.
<? super T> // T 타입과 T 타입이 상속받은 조상 클래스 타입만을 사용할 수 있음.
아래와 같이 어떤 타입('?')이 있고 그 타입이 Product의 자손이라고 선언하면, Tv객체를 저장하는 'ArrayList<Tv>' 또는 Audio객체를 저장하는 'ArrayList<Audio>'를 매개변수로 넘겨줄 수 있다.
Tv와 Audio 모두 Product의 자손이기 때문이다.
public static void printAll(ArrayList<? extends Product> list){
//Product 또는 그 자손들이 담긴 ArrayList를 매개변수로 받는 메서드
for(Unit u : list){
System.out.println(u);
}
}
다음 예제는 클래스 메소드(static method)인 cryingAnimalList() 메소드의 매개변수의 타입을 와일드카드를 사용하여 제한하는 예제이다.
import java.util.*;
class LandAnimal { public void crying() { System.out.println("육지동물"); } }
class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹");} }
class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }
class Sparrow { public void crying() { System.out.println("짹짹"); } }
class AnimalList<T> {
ArrayList<T> al = new ArrayList<T>();
public static void cryingAnimalList(AnimalList<? extends LandAnimal> al)
LandAnimal la = al.get(0);
la.crying();
}
void add(T animal) { al.add(animal); }
T get(int index) { return al.get(index); }
boolean remove(T animal) { return al.remove(animal); }
int size() { return al.size(); }
}
public class Generic03 {
public static void main(String[] args) {
AnimalList<Cat> catList = new AnimalList<Cat>();
catList.add(new Cat());
AnimalList<Dog> dogList = new AnimalList<Dog>();
dogList.add(new Dog());
AnimalList.cryingAnimalList(catList);
AnimalList.cryingAnimalList(dogList);
}
}
실행 결과
-------------------------------------------------------------------------------------
냐옹냐옹
멍멍
extends는 상속(extends)뿐 아니라 구현(implements)의 관계에서도 사용할 수 있다.
interface Info{
int getLevel();
}
class EmployeeInfo implements Info{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
public int getLevel(){
return this.rank;
}
}
class Person<T extends Info>{// extends는 상속이 무엇인가가 아니라, 부모가 누군가를 확인하는 코드이다.(implement가 아니다.)
public T info;
Person(T info){ this.info = info; }
}
public class GenericDemo {
public static void main(String[] args) {
Person p1 = new Person(new EmployeeInfo(1));
Person<String> p2 = new Person<String>("부장");
}
복수의 제네릭
class EmployeeInfo{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
}
class Person<T, S>{//복수의 제네릭을 사용할 시에는 ','를 사용한다.
public T info;
public S id;
Person(T info, S id){
this.info = info;
this.id = id;
}
}
public class GenericDemo {
public static void main(String[] args) {
Person<EmployeeInfo, int> p1 = new Person<EmployeeInfo, int>(new EmployeeInfo(1), 1);
}
}
기본 데이터 타입과 제네릭
제네릭은 참조 데이터 타입에 대해서만 사용할 수 있다. 기본 데이터 타입에서는 사용할 수 없다.
class EmployeeInfo{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
}
class Person<T, S>{
public T info;
public S id;
Person(T info, S id){
this.info = info;
this.id = id;
}
}
public class GenericDemo {
public static void main(String[] args) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = new Integer(10);
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
System.out.println(p1.id.intValue());
}
}
new Integer는 기본 데이터 타입인 int를 참조 데이터 타입으로 변환해주는 역할을 한다.
이러한 클래스를 래퍼(wrapper) 클래스라고 한다. 덕분에 기본 데이터 타입을 사용할 수 없는 제네릭에서 int를 사용할 수 있다.
제네릭의 생략
Java SE 7부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있다.
MyArray<Integer> myArr = new MyArray<>(); // Java SE 7부터 가능함.
EmployeeInfo e = new EmployeeInfo(1);
Integer i = new Integer(10);
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
Person p2 = new Person(e, i);// 제네릭 생략함
Reference
http://devbox.tistory.com/entry/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD
'Java' 카테고리의 다른 글
자바 List 컬렉션 클래스 (ArrayList) (0) | 2018.06.20 |
---|---|
자바 컬렉션 프레임워크(Collection framework) (0) | 2018.06.20 |
자바 입출력( I / O) 과 스트림(Stream) (0) | 2018.06.20 |
자바(JAVA) 공백 문자열 제거 (0) | 2018.06.12 |
java annotation 과 reflection을 사용한 xml mapping (0) | 2018.05.31 |