제네릭
목표
자바의 제네릭에 대해 학습하세요.
학습할 것 (필수)
- 제네릭 사용법
- 제네릭 주요 개념 (바운디드 타입, 와일드 카드)
- 제네릭 메소드 만들기
- Erasure
제네릭이 왜 필요할까
범용 클래스의 장점
💡타입을 한정하지 않고 클래스를 사용할 수 있음
ex) Box
매개변수를 바꿔가며 함수를 사용하는 것도 코드의 유연성이 향상된다. 이를 클래스에 접목하면 클래스의 사용이 용이할 것 같다.
그러면, 모든 자료형을 담는 구조가 있을까 ? → Object 탄생
object형식으로 참조 할 수 없는 객체는 없다. → 원시 형식의 값은 참조할 수 없다.왜냐 값은 참조하는 것이 아니라 담는것(객체와 원시 데이터는 메모리 구조상 다른 곳에 위치한다.)
primitive data type은 직접 참조가 불가능하고, Wrapper클래스로 싸서 참조하는 과정을 거쳐야 함
boxing - 지금은 직접할 필요는 없다.(알아서 해줌)
Object obj = 3; // 값은 참조되는 것이 아니라 담는것
object obj = new Integer(3);
int a = obj.intValue();
Integer가 Wrapper클래스가 된다.
boxing 한 값을 사용하려면 unboxing 하는 과정을 거쳐야하고 이 과정에서 메모리 비효율이 발생한다.
범용자료형식의 문제점
값을 꺼낼 때 타입 변환을 각각 다르게 해줘야 한다는 문제 발생
class ObjectList{
private Object[] nums;
private int current;
public ObjectList(){
nums = new Object[3];
current = 0;
}
public void add(Object num){
nums[current] = num;
current++;
}
public Object get(int index){
return nums[index];
}
}
public class Apps {
public static void main(String[] args) {
ObjectList list = new ObjectList();
list.add("박소정");
list.add(26);
System.out.println("넣는건 에러 안남");
int a = (Integer)list.get(0);//
String b = (String)list.get(1);
/*꺼낼 때 객체를 건내주고,그 안엔 여러 타입이 올 수 잇음. 그런데 String -> Integer로 하려고 하면
ClassCastException 발생!*/
}
}
int와 같은 원시 데이터 타입은 어렵고 Integer와 같은 wrapper클래스만 변수로 올 수 있음
왜냐면 Object type 이어야 함. → 그래야 모든 자료형을 담을 수 있다.
요약
- 범용 자료형의 장점 : 코드의 유연성을 높일 수 있음.→ 강제 형변환을 통해 모든 자료형 접근 가능
- 모든 자료형을 담을 수 있는 범용 클래스의 탄생 : Object
- 원시 자료형은 Object 형으로 참조 할 수 없다 : Boxing , unboxing 발생
- 모든 자료형을 담는 경우 : 문제 없음 (원시 데이터의 경우 Boxing해서 넣음)
- 담은 자료형을 꺼내는 경우 : 문제 발생 (타입 캐스팅 에러 발생의 가능성이 높음)
- 범용 자료형의 장점을 취하면서 타입의 안전성을 보장받고 싶다.
제네릭 사용법
class Box
{}
Box : 원시 타입
T : 매개변수 . 대입된 타입
Box
ArrayList<TV> test1= new ArrayList<TV>();
ArrayList<Product> test2= new ArrayList<TV>();//TV가 Product를 상속하고 있어도 다형성 X
List<TV> test1= new ArrayList<TV>();
List<TV> test2= new LinkedList<TV>();// 제네릭 클래스 단위의 다형성 허용
그럼 Product의 자손 객체만 저장하고 싶으면?
//부모 객체의 리스트로 만든 후
ArrayList<Product> product_list= new ArrayList<Product>();
product_list.add(new TV());
product_list.add(new Product());
Tv t = (TV) product_list.get(0);
Product p = product_list.get(1);//꺼낼 때 형변환 해줘야함,,
Iterator
클래스 뿐만 아니라 iterator에도 제네릭 개념이 적용될 수 있다.
public static void main(String[] args) {
ArrayList<Student> friend = new ArrayList<>();
friend.add(new Student("박소정", 26));
friend.add(new Student("유제원", 27));
friend.add(new Student("안지훈", 31));
Iterator it = friend.iterator();
while(it.hasNext()){
Student s = (Student)it.next();// Student로 형변환 해야됨,,
System.out.println(s.getName());
}
System.out.println("=================================");
Iterator<Student> it_gen = friend.iterator();
// 제네릭으로 타입 매개변수를 지정해주면 형변환 생략 가능
while(it_gen.hasNext()){
Student s = it_gen.next();
System.out.println(s.getName());
}
}
Hashmap<K,V>
데이터를 key 와 value 로 저장하는 두개의 타입을 지정해 준다
public class HashMap<k,v> extends AbstractMap<K,V>{
public V get(Object k){}
public V put(K key , V value){}// 넣을 때만 key, value 타입을 지정해주고
public V remove(Object k){}// 꺼내고 지울 때는 return 값이 value로 들어온 타입이므로
// 형변환을 하지 않아도 된다.
}
제네릭 주요개념 (바운디드 타입, 와일드 카드)
api 문서를 읽으면서 어려웠던 개념들이다.
한 종류의 타입만 저장할 수 있도록 제한 할 수 있지만, 모든 종류의 타입의 저장이 가능하다
지정할 수 있는 타입을 제한하는 방법은?
-bounded type
class FruitBox
{}
💡인터페이스를 구현해야 하는 제약이 필요해도 implements가 아닌 extends를 사용해야함!
인터페이스와 클래스를 동시에 상속 받는 경우 에는 <T extends Fruit && Interface>로 !
위의 예에서 타입간의 다형성은 컴파일 에러가 났고, 사용시 형변환을 해줘야 한다는 불편함이 있었다.
다형성을 사용하기 위해 와일드 카드라는 개념이 도입되었다.
-wildcard
<? extends T> : 상한 제한 T와 그 자손들만 가능
<? super T> : 하한 제한 T와 그 조상들만 가능
<?> : 제한 없음 → ? extends Object
ArrayList<? extends Product> = new ArrayList<TV>();// 가능
제네릭 메서드
매개 타입과 리턴 타입으로 타입 파라미터를 받는 메소드
메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메소드라 한다.
static
주의 : 둘이 모양은 같지만 다른 값이 들어 올 수 있다.
Erasure
컴파일러는 제네릭 타입을 이용해 소스파일을 체크하고, 필요한 곳에 형 변환을 넣어준다.
즉, (*.class) 에는 제네릭 타입에 대한 정보가 없다.!
(이전 파일과의 호환성을 위함)
제거 과정
→ Fruit로 치환, → Object
class Box<T extends Fruit>{
void add(T t){
}
}
//아래로 변환
class Box {
void add(Fruit t){
}
}
- 제네릭 타입을 제거한 후에 타입이 일치하지 않으면, 형변환을 추가한다.
List 의 get은 Object 타입을 반환하므로 형변환이 필요 없다.
T get(int i){
void add(T t){
}
}
// 아래로 변환
Fruit get(int i ){
return (Fruit)list.get(i);
}
- 와일드 카드가 포함되어 있는 경우
static Juice makeJuice <FruitBox<? extends Fruit> box){
String tmp = "";
for(Fruit f: box.getList()) tmp = += f +"";
return new Juice(tmp)
}
static Jucie makeJuice (FruitBox box){
String tmp = "";
Iterator it = box.getlist().iterator()
while(it.hasNest()){
tmp += (Fruit)it.next() + " " ;
}
return new Juice(tmp);
'Java > Study' 카테고리의 다른 글
[JAVA]백기선 라이브 스터디 15주차 :람다식 (0) | 2021.03.06 |
---|---|
[JAVA]백기선 라이브 스터디 13주차 JAVA I/O (0) | 2021.02.20 |
[JAVA] 백기선 라이브 스터디 12주차 Annotation (0) | 2021.02.06 |
[Java]터미널 javac 실행시 cannot find symbol처리 방법 (0) | 2021.01.28 |
[Java] 백기선 라이브 스터디 11주차 - Enum (0) | 2021.01.27 |