제네릭

배지원·2022년 10월 3일
0

JAVA

목록 보기
23/32

1. 제네릭

  • 컴파일시 타입을 체크해 주는 기능
  • 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줌
  • 참조변수와 생성자의 대입된 타입은 일치해야 한다.
ArrayList<Tv> list = new ArrayList<Tv>();
      (참조 변수)                (생성자)
ArrayList tvlist = new ArrayList();							   
tvlist.add(new Tv()); // 객체를 입력하지만 반활할때는 Object임
Tv t = (Tv)tvlist.get(0);  // (Tv)Object

-- 제네릭 사용 --
ArrayList<Tv> tvlist = new ArrayList<Tv>();							   
tvlist.add(new Tv()); // 
Tv t = tvlist.get(0);  // 반활할때도 Tv 객체로 형변환 안해줘도 됨

2. 타입 변수

  • 제네릭 클래스를 작성할 때, Object타입 대신 타입 변수(T)를 선언해서 사용
class Box{
	Object item;				// 모든 형식을 받기 위해서는 최고 조상 클래스인 Object를 사용해야한다.
    void setItem(Object item){
    	this.item = item;
    }
    Object getItem(){
    	return item;
    }
}
-- 제네릭 사용 --
class Box<T>{				// 제네릭 사용 타입 T 설정
	T item;					// T타입의 객체만 사용해야함
    						// 모든 형식을 받기 위해 Object대신 T로 치환하여 사용
    void setItem(T item){	
    	this.item = item;
    }
    T getItem(){
    	return item;
    }
}

상속 받아 제네릭 사용

class Product{ }
class Tv extends Product{}
class Audio extends Product{}

public class Test {
    public static void main(String[] args) {
        ArrayList<Product> list = new ArrayList<Product>();
        ArrayList<Tv>   tvlist = new ArrayList<Tv>();

        list.add(new Product());        // 자기 자신과 상속 받은 객체의 타입 사용 가능
        list.add(new Tv());        
        list.add(new Audio());

    
        tvlist.add(new Tv());           // 자손타입 이므로 부모와 다른 자손의 타입 사용 불가능   
        tvlist.add(new Product());      // 오류 발생
        tvlist.add(new Audio());        // 오류 발생
    }

}

3. lterator / HashMap

(1) lterator< T >

  • 클래스를 작성할 때, Object타입 대인 T와 같은 타입 변수를 사용
public class lterator {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<Student>();
        list.add(new Student("자바왕",1,1));
        list.add(new Student("자바짱",1,2));
        list.add(new Student("홍길동",2,1));

        Iterator<Student> it = list.iterator();

        while(it.hasNext()){
          // Student s = (Student)it.next(); 제네릭 사용하지 않으면 형변환 필요
            Student s = it.next();
            System.out.println(s.name);
        }
    }
}

class Student{
    String name ="";
    int ban;
    int no;

    Student(String name, int ban, int no){
        this.name = name;
        this.ban = ban;
        this.no = no;
    }
}

(2) HashMap< K,v >

  • 여러 개의 타입 변수가 필요한 경우, 콤마(,)를 구분자로 선언
HashMap<String, Student> map = new HashMap<String, Student>();	// 생성
map.put("자바왕", new Student("자바왕",1,1,100,100,100)); // 값 저장

4. 제한된 제네릭 클래스

  • extends로 대입할 수 있는 타입을 제한

(1) 부모 자식 상속 관계

class FruitBox<T extends Fruit>{	// Fruit의 자손만 타입으로 지정 가능
	ArrayList<T> list = new ArrayList<T>();
}

FruitBox<Apple> appleBox = new FruitBox<Apple>(); // 가능
FruitBox<Toy> toyBox = new FruitBox<Toy>();	// 불가능 Toy는 Fruit의 자손이 아님

(2) interface 관계

interface  Eatable{}
class FruitBox<T extends Eatable>();	// implements가 아니라 extends를 사용함 

클래스,Interface 상속을 받는 예제

interface Eatable{ }

class Fruit implements Eatable{
    public String toString(){
        return "Fruit";
    }
}

class Apple extends Fruit{
    public String toString(){
        return "Apple";
    }
}

class Grape extends Fruit{
    public String toString(){
        return "Grape";
    }
}

class Toy{
    public String toString(){
        return "Toy";
    }
}


public class Ex12_3 {
    public static void main(String[] args) {
        FruitBox<Fruit> fruitBox = new FruitBox<>();        // FruitBox 객체 생성
        FruitBox<Apple> appleBox = new FruitBox<>();        
        FruitBox<Grape> grapeBox = new FruitBox<>();
   //   FruitBox<Toy> fruitBox = new FruitBox<>();    에러 발생 FruiteBox의 제네릭에는 Fruit와 Eatable의 자손 참조만 올 수 있음
        Box<Toy> toyBox = new Box<>();                      

        fruitBox.add(new Fruit());          // 객체 생성할 때 Fruit클래스에 관한 값만 받을 수 있도록 함(자손 클래스 포함)
        fruitBox.add(new Apple());
        fruitBox.add(new Grape());
        appleBox.add(new Apple());
     // appleBox.add(new Fruit());          객체 생성할때 Apple에 관련된 값만 받을 수 있도록 함
        grapeBox.add(new Grape());
        toyBox.add(new Toy());
    }
}

class Box<T>{
    ArrayList<T> list = new ArrayList<>();

    void add(T item){
        list.add(item);
    }

    T get(int i){
        return list.get(i);
    }

    int size(){
        return list.size();
    }

    public String toString(){
        return list.toString();
    }
}

class FruitBox<T extends Fruit & Eatable> extends Box<T>{ } // Box를 상속받으며 제네릭을 통해 Fruit, Eatable클래스를 상속받는 객체만 가져옴

5. 제네릭의 제약

(1) static멤버에 타입 변수 사용 불가

class Box<T>{
	static T item;		// 오류 발생
    static int compare(T t1, T t2){}    // 오류 발생
}

(2) 배열 생성할 때 타입 변수 사용불가. 타입 변수로 배열선언은 가능

class Box<T>{
	T[] itemArr;
    	...
    T[] toArray(){
    	T[] tmpArr = new T[itemArr.length];	// 에러 발생
    }
}

5. 와일드 카드 < ? >

  • 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
  • 하나의 참조변수로 서로 다른 타입이 대입된 여러 제네릭 객체를 다루기 위한 것
ArrayList<? extends Product> list = new ArrayList<Tv>();  // ok
ArrayList<? extends Product> list = mew ArrayList<Audio>();  // ok
ArrayList<Product> list = new ArrayList<Tv>();	// 에러, 대입된타입 불일치

-------
<? extends T>	와일드 카드의 상한 재한. T와 그 자손들만 가능
<? super T>     와일드 카드의 하한 제한. T와 그 조상들만 가능
<?>				제한 없음. 모든 타입이 가능.<? extends Object>외 동일
  • 매서드의 매개변수에 와일드 카드를 사용
class Fruit{ public String toString(){ return "Fruit";}}

class Apple extends Fruit { public String toString(){ return "Apple";}}

class Grape extends Fruit{ public String toString(){ return "Grape";}}


class Juice{
    String name;
    Juice(String name){this.name = name + "Juice";}
    public String toString(){return name;}
}

class Juicer{
    static Juice makeJuice(FruitBox<? extends Fruit> box){
        String tmp = "";

        for(Fruit f : box.getList())
            tmp += f +"";
        return  new Juice(tmp);
    }
}

public class No12_1 {
    public static void main(String[] args) {
        FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
    //    FruitBox<T extends Fruit> appleBox = new FruitBox<T>();     // 오류 발생
        FruitBox<? extends Fruit> appleBox;    // 와일드 카드 사용하여 Fruit를 상속받거나 자기 자신은 올 수 있도록 함 

        appleBox = new FruitBox<Fruit>();   // 자기 자신
        appleBox = new FruitBox<Apple>();   // Apple
        appleBox = new FruitBox<Grape>();   // Grape 모두 사용 가능

        fruitBox.add(new Apple());
        fruitBox.add(new Fruit());
        fruitBox.add(new Grape());

    }
}

class FruitBox<T extends Fruit> extends Box<T>{}

class Box<T>{
    ArrayList<T> list = new ArrayList<>();
    void add(T item){list.add(item);}

    T get(int i){return list.get(i);}

    ArrayList<T> getList(){return list;}

    int size() {return list.size();}

    public String toString(){return list.toString();}
}

6. 제네릭 메서드

  • 제네릭 타입이 서언된 메서드(타입 변수는 메서드 내에서만 유효)
  • 메서드를 호출할 때마다 다른 제네릭 타입을 대입할 수 있게 한 것
static <T> void sort(List<T> list, Comparator<? super T> c)
  • 클래스의 타입 매개변수 < T >와 메서드의 타입 매개변수 < T >는 별개
class FruitBox<T>{
	static<T> void sort(List<T> list, Comparator<? super T> c){
    	...
    }
}

- iv,lv와 같이 가까이에 있는 매개변수가 우선임 즉, 첫번째 줄에 있는 매개변수와 2번째 줄에 있는 매개변수의 
  타입문자는 일치하지만 다른 타입변수임

profile
Web Developer

0개의 댓글