
🎯 목표 : 제네릭의 개념과 활용 이해
📒 Generics
👉 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시 타입체크를 해주는 기능
ArrayList< E > list = new ArrayList< E >(); // 뒤의 T는 생략가능하다
// 타입 변수는 T,E등 다른것을 사용해도된다
- <>에 데이터 타입을 정의하여 선언한다.
📌 특징
- 제네릭 객체를 생성할때 클래스 간의 다형성은 성립되며, 양 변의 데이터 타입 T는 동일해야된다.
- 타입의 안정성을 보장해주는 기능을 한다. 의도하지 않은 타입의 객체가 저장되는 것을 막고 저장된 객체를 꺼내올때 원래의 타입과 다른 타입으로 잘못 형변환 되어 발생할수 있는 에러를 줄일수 있다.
- 즉 다룰 객체의 타입을 미리 명시해줘 객체를 꺼내어 사용할때 타입 체크, 형변환 등 번거로운 작업을 줄여주고 에러를 방지한다.
📌 선언
class CageList< T > {
ArrayList<T> list = new ArrayList<>();
void setCage (T animal){
list.add(animal); }
T getCage(int index) {
return list.get(index);
}
}
- <T> T 데이터 타입만 저장가능하다.
- <T extends Class> Class의 자신을 포함한 모든 자식 데이터 타입이 저장가능하다.
- <T extends Interface> extends 키워드를 사용하여 해당 인터페이스를 구현한 데이터 타입 저장이 가능하다는것을을 명시해줄수 있다.
- <T extends Class & Interface> Class 자신을 포함한 자식 클래스중 인터페이스를 구현한 타입만 저장 가능하다는 것을 명시해준다.
- 클래스에 타입변수로 제네릭을 선언하지 않고 클래스 내부의 메소드에만 타입변수로 제네릭을 선언할수 있다.
- 클래스에 타입변수로 제네릭을 선언한 경우 클래스 내부에 Static 멤버는 타입변수를 선언할수 없다. (제네릭의 제한)
class CageList<T> {
ArrayList<T> list = new ArrayList<>();
static void setCage (T animal){ // 에러발생
list.add(animal);
}
T getCage(int index) {
return list.get(index);
}
}
- Static 멤버는 선언된 제네릭 타입변수와 상관없이 동일한 것이어야 하기 때문에 에러가 발생한다.
👉 선언 예제
import java.util.ArrayList;
interface Thisinfo { String info();}
class Animal implements Thisinfo { public String info(){return "This Animal";} }
class Cat extends Animal implements Thisinfo { public String info() {return "This Cat";} }
class Dog extends Animal implements Thisinfo { public String info() {return "This Dog";} }
class Cow extends Animal { }
class Person implements Thisinfo { public String info() {return "This Person";} }
class CageList<T extends Animal> {
ArrayList<T> list = new ArrayList<>();
void setCage (T animal){
list.add(animal); }
T getCage(int index) {
return list.get(index);
}
}
public class Study {
public static void main(String[] args) {
CageList <Animal> list = new CageList<>();
list.setCage(new Cat());
list.setCage(new Dog());
list.setCage(new Cow()); //부모 클래스가 인터페이스를 구현했기 때문에 저장가능하다.
// list.setCage(new Person()); // 자식 클래스가 아니기때문에 에러발생.
for (Animal i : list.list) {
System.out.println(i);
}
}
}
- Interfacd Thisinfo를 구현한 Animal, Cat, Dog, Person 클래스가 있다.
- CageList는 ArrayList 내부에 생성하여 일부 기능을 구현하도록 만들었다.
- CageList 클래스는 <T extends Animal & Thisinfo> 를 Generic 선언을 하였다.
- CageList 객체를 생성할때 Generic의 데이터 타입으로는 Animal 와 상속관계이고 인터페이스 Thisinfo를 구현한 클래스만 데이터 타입으로 저장가능하다.
- public 클래스 부분에서 확인해보면, Animal의 상속을 받은 Cat,Dog,Cow 객체는 생성되어 데이터로 저장될수 있다.
- Person은 Animal과 상속관계가 아니기 때문에 호출되어 저장할수 없다.
- 만약 아래와 같이 CageList 객체를 Person 데이터 타입으로 생성하려하면 에러가 발생할것이다. Person은 제네릭으로 선언한 클래스 범위에 포함되지 않는다.
CageList<Person> list1 = new CageList<Person>();
📌 와일드 카드 <?>
class Tostr {
static String str = "";
static String listtoString(CageList<? extends Animal> list) {
for (int i=0; i<list.cageSize(); i++){
str += ", " + list.getCage(i).info();
}
str = str.substring(1);
return str;
}
}
- 위와 같이 CageList 요소의 info 메소드를 호출하여 출력값을 나열하는 클래스를 만들었다.
- Tostr 클래스 내부의 메소드는 static 메소드며, 타입변수를 사용할수 없고 데이터 타입을 확정지어줘야된다.
- 메소드의 데이터 타입을 확정 지어 주게되면, 해당데이터 타입 외에는 Tostr 클래스의 listtoString 메소드에 접근할수 없게된다.
- 선언 예제에서 상속관계를 가지고 있는 여러 Animal 자식 클래스들이 CageList 클래스에 참조되어 생성되었을때 Animal 상속관계의 데이터 타입이 listtoString 메소드에 접근할수 있게 만들기 위해서 와일드 카드로 상속관계의 데이터 타입은 접근할수 있도록 만들었다.
- 와일드 카드의 선언 방식은 아래와 같다.
< ? > // 제한 없이 모든 타입 가능 Object의 자식 클래스는 모두 가능.
< ? super T > // T를 포함한 부모 클래스들만 가능하다.
< ? extends T >// T를 포함한 자식 클래스들만 가능하다.
📌 제네릭 타입의 제거
- 컴파일러는 제네릭 타입을 제거하고 필요한 부분에 자동으로 형변환을 시킨다.
- 제네릭스 기능 자체가 컴파일 단게에서 런타임 에러를 발견하기 위한 기능이기 때문이고,
- 이런 방식이 성능적으로는 떨어지지만 하위 호환성 때문에 컴파일 단계에서 제네릭 타입을 제거한다.
'Language > JAVA' 카테고리의 다른 글
| Java Annotation (1) | 2022.09.04 |
|---|---|
| Java 열거형 (0) | 2022.09.03 |
| Java Memory Structure (0) | 2022.09.03 |
| Java Object, Class, Instance_02 - OOP (0) | 2022.09.02 |
| Java Collection Framework 요약 (0) | 2022.08.29 |