- 자바의 정석 135~141강 ch12 지네릭스, 열거형, 애너테이션(1)2023년 09월 12일 08시 57분 15초에 업로드 된 글입니다.작성자: 민발자728x90
ch 12-1 지네릭스 Generics 란
1. 지네릭스
컴파일시 타입을 체크해 주는 기능
// jdk 1.5 이전 ArrayList list = new ArrayList(); list.add(10); list.add(20); list.add("30"); // String 추가 Integer i = (Integer)list.get(2); // 컴파일 OK /* 실행하면 에러, ClassCastException 발생, 형변환예외 컴파일러의 한계, 꺼낼때 Object라 컴파일에러 안뜨지만 실행시 형변환 에러발생 실행시 에러는 프로그램이 종료되고 컴파일 에러는 실행전에 에러가 뜨기때문에 컴파일 에러가 더 나음 지네릭스를 통해 컴파일 에러로 이끌어냄 */ // jdk 1.5 이후 지네릭스 도입 ArrayList<Integer> genericList = new ArrayList<Integer>(); genericList.add(10); genericList.add(20); genericList.add("30"); // 저장 객체 타입을 지정해줬기때문에 컴파일 에러 발생 //꺼낼때 형변환 필요없음-지정해줬기 때문에 지정 타입으로 받아주면 됨 Integer i = genericList.get(1); ArrayList<Object> objectList = new ArrayList<Object>(); // 여러 타입을 저장하고 싶으면 Object를 지정, 상황에 따라 형변환 필요
2. 지네릭스 장점
타입 안정성 제공, 타입체크와 형변환을 생략 가능해 코드 간결
ch 12-2~3 타입 변수
1. 타입변수
지네릭 클래스 작성 시, Object 타입 대신 타입 변수 E를 선언해 사용
2. 타입변수에 대입하기
객체 생성 시 타입변수 E 대신 실제 타입을 지정해 대입
ArrayList list = new ArrayList(); list.add(new Tv()); list.add(new Audio()); Tv t = (Tv)list.get(0); // list의 첫번째요소 꺼냄, 반환타입이 Object라 형변환필요 ArrayList<Tv> genericList = new ArrayList<Tv>(); // Tv객체만 저장 가능 genericList.add(new Tv()); // genericList.add(new Audio()); 타입변수를 지정해서 Tv객체 이외의 객체는 저장 불가 t = genericList.get(0); // 첫번째 요소 꺼냄, 반환타입이 모두 Tv라 형변환 불필요
ch 12-4~6 지네릭스 용어, 지네릭 타입과 다형성
1. 지네릭스 용어
Box<T> : 지네릭 클래스 T의 Box 또는 T Box라고 읽음 T : 타입 변수 또는 타입 매개변수 Box : 원시타입 raw type
2. 지네릭 타입과 다형성
참조변수와 생성자의 대입된 타입은 일치해야함, 부모 자식 관계여도 불일치하면 에러 무조건 일치해야함(다형성 안됨)
ArrayList<Tv> list = new ArrayList<Tv>(); ArrayList<Product> list = new ArrayList<Tv>(); // 에러
지네릭 클래스간 다형성은 성립, 대입된 타입은 일치해야함(다형성 안됨)
List<Tv> list = new ArrayList<Tv>(); // 지네릭 클래스간의 다형성은 가능, ArrayList가 List 구현 List<Tv> list = new LinkedList<Tv>(); // LinkedList가 List 구현
매개변수의 다형성 성립
ArrayList<Product> list = new ArrayList<Product>(); list.add(new Product()); list.add(new Tv()); list.add(new Audio()); // Product의 자손인 Tv, Audio 가능 Product p = list.get(0); // 타입일치해 형변환 필요없음 Tv t = (Tv)list.get(1); // 저장되어있는 객체는 Tv객체이지만 반환타입이 Product이므로 형변환 필요
ch 12-7~8 Iterator, HashMap과 지네릭스
1. Iterator<E>
클래스 작성 시 Object타입 대신 타입 변수 사용
// 지네릭 도입 이전 Iterator it = list.iterator(); while(it.hasNext()) { Student s = (Student)it.next(); // 반환 타입이 Object 형변환 필요 ... } // 지네릭 도입 이후 Iterator<Student> it = list.iterator(); while(it.hasNext()) { Student s = it.next(); // 반환 타입이 Student 형변환 불필요 ... }
2. HashMap<K, V>
타입 변수가 여러 개인 경우 ,를 구분자로 선언
HashMap<String, Student> map = new HashMap<String, Student>(); map.put("김", new Student("김", 1, 1, 100, 100)); Stuent s1 = (Stuent)map.get("1-1"); // 타입 불일치로 형변환 필요 Stuent s2 = map.get("1-1"); // 형변환 불필요
ch 12-9~11 제한된 지네릭 클래스, 지네릭스의 제약
1. 제한된 지네릭 클래스
extends로 대입할 수 있는 타입을 제한
class FruitBox<T extends Fruit> {} // Fruit의 자손만 타입으로 지정 가능 FruitBox<Apple> appleBox = new FruitBox<Apple>(); FruitBox<Toy> appleBox = new FruitBox<Toy>(); // Fruit의 자손이 아니기 때문에 에러
인터페이스도 extends이용해 제한 가능
interface Eatable {} class FruitBox<T extends Eatable> {} // 대입할 수 있는 타입 제한
2. 지네릭스의 제약
타입 변수에 대입은 인스턴스 별로 다르게 가능, 생성할 때마다 다르게 지정 가능
static멤버에 타입 변수 사용 불가 - 모든 인스턴스에 공통이기 때문에
배열 생성할 때 타입변수 사용불가 - 생성자 new는 타입이 확정되어 있어야 함
Box<Apple> appleBox = new Box<Apple>(); // Apple만 저장 가능 Box<Grape> grapeBox = new Box<Grape>(); // Grape만 저장 가능 // 생성할 때마다 다르게 지정 가능 class Box<T> { static T item; // 에러 static멤버는 타입변수 사용 불가 T[] itemArr; // T타입의 배열을 위한 참조 변수는 가능 T[] toArray() { T[] tmpArr = new T[itemArr.length] // 에러 지네릭 배열 생성 불가 new T가 불가 // 생성자 new는 타입이 확정되어 있어야 한다 } }
ch 12-12~14 와일드카드, 지네릭 메서드
1. 와일드 카드 <?>
하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
<? extends T> 와일드 카드의 상한 제한, T와 그 자손만 가능 <? super T> 와일드 카드의 하한 제한, T와 그 조상만 가능 <?> 제한 없음 모든 타입 가능<? extends Object>와 동일 ArrayList<? extends Product> list = new ArrayList<Tv>(); // Product와 자손들(Tv, Audio) ArrayList<? super Tv> list = new ArrayList<Tv>(); // Tv와 조상(Product, Object)
2. 메서드 매개변수에 와일드 카드 사용
static Juice makeJuice(FruitBox<? extends Fruit> box) { ... } // 매개변수로 Fruit와 Fruit의 자손들 가능
3. 지네릭 메서드
지네릭 타입이 선언된 메서드(타입 변수는 메서드 내에서만 유효)
클래스 타입 매개변수<T>와 메서드의 타입변수 <T>는 별개
class Box<T> { // 지네릭 클래스 // 지네릭 메서드 static <T> void sort(List<T> list, Comparator<? super T> c){ ... } // 타입문자 일치하나 다른 타입 변수 // 지네릭 메서드 안에서는 메서드 타입변수가 우선 iv와 lv와 동일 }
메서드 호출할 때마다 타입을 대입(대부분 생략 가능)
메서드 호출 시 타입을 생략하지 않을 때는 클래스 이름 생략 불가(타입 생략하는 경우는 매우 드뭄)
System.out.println(Jucie.makeJuice(appleBox)); // 타입 생략가능 System.out.println(<Fruit>makeJuice(appleBox)); // 에러 타입 지정시 클래스 이름 필수 System.out.println(Jucie.<Fruit>makeJuice(appleBox)); // 생략 안할 땐 클래스 이름 필수 System.out.println(this.<Fruit>makeJuice(appleBox)); // this 사용가능
4. 와일드카드와 지네릭 메서드 정리
와일드 카드는 하나의 참조변수로 서로 다은 타입이 대입된 여러 지네릭 객체를 다루기 위한 것
지네릭 메서드는 메서드를 호출할 때마다 다른 지네릭 타입을 대입할 수 있게 한 것
ch 12-15~16 지네릭 형변환
1. 지네릭 타입의 형변환
지네릭 타입과 원시 타입 간의 형변환은 좋지 않음
Box1 box = null; // 원시타입 Box1<String> bStr = null; // 지네릭 타입 box = (Box1)bStr; // 지네릭 -> 원시 bStr = (Box1<String>)box; // 원시 -> 지네릭 // 가능하지만 경고 box.add(new Integer(100)); // 참조변수가 원시타입이기 때문에 String 지네릭 타입이어도 Integer가 저장된다 Box1<Object> objBox = null; Box1<String> strBox = null; // objBox = (Box1<Object>)strBox; 불가능 형변환 안됨 // strBox = (Box1<String>)objBox; 불가능 형변환 안됨
와일드 카드가 사용된 지네릭 타입으로는 형변환 가능
// 와일드 카드 사용된 타입으로 형변환가능 Box1<? extends Object> wBox = (Box1<? extends Object>)new Box1<String>(); Box1<? extends Object> wBox = new Box1<String>(); // 형변환 생략가능 Box1<? extends Object> wBox2 = new Box1<Object>(); strBox = (Box1<String>)wBox2; // 반대도 형변환 가능
2. 지네릭 타입의 제거
컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환 넣음
Object → <T>로 지정한 것을 다시 <T> → Object로 변경
자바 버전 하위 호환성때문에 컴파일러가 지네릭 타입을 제거함 → 지네릭 1.5부터 적용됨
①지네릭 타입의 경계를 제거
②지네릭 타입 제거 후 타입이 불일치하면 형변환 추가
③와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가
728x90'정리 > Java' 카테고리의 다른 글
자바의 정석 148~157강 ch13 쓰레드 (1) 2023.10.12 자바의 정석 142~147강 ch12 지네릭스, 열거형, 애너테이션(2) (0) 2023.09.16 자바의 정석 127~134강 ch11 컬렉션 프레임웍(2) (0) 2023.09.10 자바의 정석 119~126강 ch11 컬렉션 프레임웍(1) (0) 2023.09.04 자바의 정석 연습문제 ch 9 java.lang 패키지와 유용한 클래스 (3) 2023.09.03 다음글이 없습니다.이전글이 없습니다.댓글