minghxx.blog
  • 자바의 정석 135~141강 ch12 지네릭스, 열거형, 애너테이션(1)
    2023년 09월 12일 08시 57분 15초에 업로드 된 글입니다.
    작성자: 민발자
    728x90

    자바의 정석 기초편(2020최신)

    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
    댓글