프로그래밍 언어&프레임워크/java
[Java/자바] 제네릭 프로그래밍
Coblin
2021. 9. 13. 00:31
반응형
들어가기 전에
본 글은 제가 참여하고 있는 자바 기초 스터디에서 제가 발표한 내용으로 위의 책 Chapter 12 컬렉션 프레임워크 파트를 정리한 글입니다.
틀린 내용이나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.
제네릭(Generic) 프로그래밍
- 변수의 선언이나 메서드의 매개변수를 하나의 자료형이 아닌 여러 자료형으로 변환될 수 있도록 프로그래밍하는 방식
ex) String[] strArray = {"strOne", "strTwo"}; Int[] intArray = {1, 2};
- 데이터 형식에 의존하지 않고 하나의 값이 여러 다른 데이터 타입들을 가질 수 있는 기술에 중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식
- 실제 사용되는 참조 자료형으로의 변환은 컴파일러가 검증하므로 안정적인 프로그래밍 방식
- 컬렉션 프레임워크에서 많이 사용됨
제네릭클래스 <적용할타입> 변수 = new 제네릭클래스<적용할타입>();
타입 매개변수 | 설명 |
E | 원소 (Element) |
K | 키 (Key) |
N | 숫자 (Number) |
T | 타입 (Type) |
V | 값 (Value) |
제네릭의 제약
- 기본 타입을 제네릭 인수로 사용 불가
- 정적 제너릭 타입 금지
- 제너릭 타입의 인스턴스화 금지. 즉, new T() 등 금지
- 제네릭 타입의 배열 생성 금지
- 실행 시간에 제네릭 타입 점검 금지 예를 들어, a instance of ArrayList
- 제네릭 클래스의 객체는 예외로 던지거나 잡을 수 없다.
- 제네릭의 서브타입 허용 않음
사용 예시
Object타입으로 모든 자료형 사용하기
// 한식 일식 타입을 모두 담을 수 있는 Food 클래스 정의
public class Food {
// 모든 자료형을 받을 수 있는 Object 타입으로 정의
// 이 경우 사용시 캐스팅 필요
private Object type;
public Object getType() {
return type;
}
public void setType(Object type) {
this.type = type;
}
@Override
public String toString() {
return "Food [type=" + type + "]";
}
}
// 한식 클래스 정의
public class Korean {
@Override
public String toString() {
return "한식";
}
}
// 동작
public class FoodTest {
public static void main(String[] args) {
Food food = new Food();
food.setType(new Korean());
// food.getType이 반환하는 타입은 Object이기 때문에 캐스팅 필요
Korean korean = (Korean)food.getType();
System.out.println(korean); // 한식
}
}
제네릭 타입 사용
// 제네릭 클래스 T는 type의 약자, 자료형 매개변수
public class Food<T> {
// T는 Object타입으로 변환
private T type;
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
}
@Override
public String toString() {
return "Food [type=" + type + "]";
}
}
public class FoodTest {
public static void main(String[] args) {
// 해당영역을 보고 컴파일러가 캐스팅작업 수행
Food<Korean> food = new Food<Korean>();
food.setType(new Korean());
// food를 선언 할 때 Korean 타입을 사용한다고 작성했기 때문에
// 캐스팅 작업 불필요
Korean korean = food.getType();
System.out.println(korean);
}
}
제한된 타입의 제네릭 메서드
// Type을 상속받은 클래스만 사용 가능하게 제한
public class Food<T extends Type> {
private T type;
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
}
@Override
public String toString() {
return "Food [type=" + type + "]";
}
}
public class Korean extends Type{
@Override
public String toString() {
return "한식";
}
}
public class Japanese extends Type{
@Override
public String toString() {
return "일식";
}
}
public class Chinese {
@Override
public String toString() {
return "중식";
}
}
public class FoodTest {
public static void main(String[] args) {
Food<Korean> food = new Food<Korean>();
food.setType(new Korean());
Korean korean = food.getType();
Food<Japanese> food2 = new Food<Japanese>();
food2.setType(new Japanese());
Japanese japanese = food2.getType();
// Chinese클래스는 Type을 상속받지 못해 사용 불가능
// Bound mismatch: The type Chinese is not a valid substitute for the bounded parameter <T extends Type> of the type Food<T>
Food<Chinese> food3 = new Food<Chinese>();
food3.setType(new Chinese());
Chinese chinese = food3.getType();
}
}
자료형 매게변수 T
- type의 의미로 T를 많이 사용
- <T>에서 <>는 다이아몬드 연산자
- static 키워드에서는 사용 불가
T는 생성될 때(new) 타입이 결정되는데 static은 인스턴스 생성과 상관없이 메모리를 잡기 때문에 사용불가 ArrayList<String> list = new ArrayList<>();
다이아몬드 연산자 내부에서 자료형 생략 가능- 제네릭에서 자료형 추론(자바 10부터 가능)
ArrayList<String> list = new ArrayList<String>();
→var list = new ArrayList<String>();
제네릭에서 대입된 자료형을 명시하지 않는 경우
Food food = new Food();
food.setType(new Korean());
// food의 타입(<Korean>)을 명시하지 않았기 때문에 형변환 필요
Korean korean = (Korean)food.getType();
반응형