강한 타입 체크(strong type checking)
-제네릭을 사용하면 모든 타입의 상품을 저장할 수 있으면서도 잘못된 캐스팅을 할 때 문법 오류를 발생시켜 잘못된 캐스팅으로 발생할 수 있는 문제를 사전에 예방
1. 제네릭 클래스와 제네릭 인터페이스 정의하기
- 클래스명 다음에 <제네릭 타입 변수명> 삽입
- 제네릭 클래스
//제네릭 타입 변수명이 1개 일때
접근 지정자 class 클래스명 <T> {
//타입 T를 사용한 코드
}
//제네릭 타입 변수명이 2개 일때
접근 지정자 class 클래스명 <K,T> {
//타입 K,T를 사용한 코드
}
public class MyClass<T> {
private T t;
public T get(){
return t;
}
public void set(T t) {
this.t=t;
}
}
- 제네릭 인터페이스
//제네릭 타입 변수명이 1개 일떄
접근 지정자 interface 클래스명 <T> {
//타입 T를 사용한 코드
}
//제네릭 타입 변수명이 2개 일때
접근 지정자 interface 클래스명 <T,V> {
//타입 K,V를 사용한 코드
}
public interface MyInterface<K,V> {
public abstract void setKey(K k);
public abstract void setValue(V v);
public abstract K getKey();
public abstract V getValue();
}
- 제네릭 타입 변수의 관례적 표기 및 의미
제네릭 타입 변수 | 의미 |
T | 타입(Type) |
K | 키(Key) |
V | 값(Value) |
N | 숫자(Number) |
E | 원소(Element) |
2. 제네릭 클래스의 객체 생성
- 객체를 생성할 떄 제네릭 타입 변수에 실제 타입을 대입
클래스명<실제 제네릭 타입> 참조 변수명 = new 클래스명<실제 제네릭타입>();
클래스명<실제 제네릭 타입> 참조 변수명 = new 클래스명<>();
- 제네릭 타입 변수 1개를 가진 제네릭 클래스의 선언 및 활용
->제네릭 클래스 MyClass<T>를 정의한 후 객체를 생성해 사용
->제네릭 클래스는 클래스를 정의하는 시점에 타입을 지정하는 것이 아니라 객체를 생성하는 시점에 타입을 지정하기 때문에 하나의 제네릭 클래스로 다양한 타입의 객체를 저장 및 관리할 수 있는 객체 생성할 수 있음
(mc1 객체 - string 타입을 저장 및 관리하는 객체
mc2 객체 - integer 타입을 저장 및 관리하는 객체)
package GenericArguement;
class MyClass<T>{
private T t;
public T get() { //getter 메서드
return t;
}
public void set(T t) { //setter 메서드
this.t=t;
}
}
public class GenericArgument {
public static void main(String[] args) {
MyClass<String>mc1 = new MyClass<String>(); //String 타입을 저장하거나 꺼내 올 수 있는 객체로 지정
mc1.set("안녕");
System.out.println(mc1.get());
MyClass<Integer>mc2 = new MyClass<>(); //Integer 타입을 저장하거나 꺼내 올 수 있는 객체로 지정
mc2.set(100);
System.out.println(mc2.get());
//MyClass<Integer>mc3 = new MyClass<>();
//mc3.set("안녕"); //강한 타입 체크로 문법 오류 발생
}
}
- 제네릭 타입 변수 2개를 가진 제네릭 클래스의 선언 및 활용
package GenericArguement;
class KeyValue<K,V>{
private K key;
private V value;
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setKey(K key) {
this.key=key;
}
public void setValue(V value) {
this.value=value;
}
}
public class GenericArgument {
public static void main(String[] args) {
KeyValue<String,Integer>kv1 = new KeyValue<>(); //제네릭 타입 변수 K,V가 각각 String,Integer 타입으로 결정
kv1.setKey("사과");
kv1.setValue(1000);
String key1 = kv1.getKey();
int value1 = kv1.getValue();
System.out.println("key:"+key1+"value:"+value1);
KeyValue<Integer,String>kv2 = new KeyValue<>(); //제네릭 타입 변수 K,V가 각각 Integer, String 타입으로 결정
kv2.setKey(404);
kv2.setValue("Not Found(요청한 페이지를 찾을 수 없습니다.)");
int key2 = kv2.getKey();
String value2 = kv2.getValue();
System.out.println("key:"+key2+"value:"+value2);
KeyValue<String,Void> kv3 = new KeyValue<>(); //void : 해당 제네릭 타입 변수의 필드를 사용하지 않음
kv3.setKey("키 값만 사용");
String key3 = kv3.getKey();
System.out.println("key:"+key3);
}
}
- 제네릭의 필요성
1) 추가 클래스 생성 없이 어떤 상품도 저장 및 관리 -> object 타입으로 선언
2) setter 메서드에 잘못된 객체를 입력했을 때 바로 문법으로 체크 ->제네릭
3) getter 메서드의 리턴 타입도 다운캐스팅이 필요 없어야 함
-> 제네릭
- 해결책 2. 제네릭 클래스를 사용한 다양한 객체의 저장
-> goods1일때 제네릭 타입을 Apple타입, goods2일때 제네릭 타입을 Pencil타입으로 설정
package GenericArguement;
class Apple{}
class Pencil{}
class Goods<T>{
private T t;
public T get() {
return t;
}
public void set(T t) {
this.t=t;
}
}
public class GenericArgument {
public static void main(String[] args) {
//1.Goods를 이용해 Apple 객체를 추가하거나 가져오기
Goods<Apple>goods1 = new Goods<>();
goods1.set(new Apple());
Apple apple = goods1.get(); //다운 캐스팅 필요없음
//2.Goods를 이용해 pencil 객체를 추가하거나 가져오기
Goods<Pencil>goods2 = new Goods<>();
goods2.set(new Pencil());
Pencil pencil =goods2.get();
//3.잘못된 타입 선언
Goods<Apple>goods3 = new Goods<>();
goods3.set(new Apple());
//Pencil pencil2 = goods3.get(); //강한타입체크로 문법 오류 발생
}
}
'Java > 문법' 카테고리의 다른 글
16 - 4장 제네릭 타입 범위 제한 (0) | 2023.06.16 |
---|---|
16 -3장 제네릭 메서드 (0) | 2023.06.15 |
16 -1장 제네릭 클래스와 제네릭 인터페이스 (0) | 2023.06.15 |
15 - 5장 쓰레드의 상태 (0) | 2023.06.15 |
10 - 6장 최상위 클래스 Object (0) | 2023.06.01 |