자바 스터디_14주차 지네릭스

이경환·2023년 2월 12일
0

JAVA

목록 보기
6/6

제네릭 사용법

  • 지네릭스의 장점
    1.타입 안정성 제공 -> 컴파일 단계에서 잡을 수 있다.
    항상 프로그래머는 런타임 에러를 컴파일 에러에서 잡는 것이 좋다. 지네릭스는 런타임 에러인 ClassCastException 을 예방 할 수 있다.
    2.타입체크와 형변환의 번거로움이 줄어든다.
package JavaJungSuk3_Study.Example.java8;

import java.util.ArrayList;
import java.util.List;

public class GenericsExample_1 {
    public static void main(String[] args) {
        List<Tv> list = new ArrayList<>(); //지네릭스 타입을 선언
        list.add(new Tv());
//      list.add(new Audio()); //지네릭스 이전에는 컴파일 단계에서 잡는게 불가능했다.

        List<Tv> list2 = new ArrayList<>();
        list2.add(new Tv());
        Tv tv1 = list2.get(0); 
        List list3 = new ArrayList<>(); //생성시 지네릭스타입을 선언해주지 않을경우
        list3.add(new Tv());
        Tv tv2 = (Tv) list3.get(0);      // 형변환을 해줘야 하는 번거로움이 생긴다.
    }
}

class Tv {
}

class Audio {
}
  • 타입 변수
    MyClass 에서 T 를 "타입 변수(type variable)" 라고 한다. 지네릭스 도입이전에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴타입으로 Object 타입의 참조변수를 많이 사용했다. 하지만 지네릭스가 도입된후 다양한 타입을 지정해 사용가능.
package JavaJungSuk3_Study.Example.java8;

import java.util.ArrayList;
import java.util.List;

public class GenericsExample_2 {
    public static void main(String[] args) {
        List<Tv2> list = new ArrayList<>();
        list.add(new Tv2());
        list.get(0).setPassive("passive");
        System.out.println(list.get(0).getPassive());
    }
}

class Tv2<T> {  //이런식으로 대입이가능 object를 모두 t로 바꾼다
    T passive;

    public T getPassive() {
        return passive;
    }

    public void setPassive(T passive) {
        this.passive = passive;
    }
}

class Audio2<E> {  //이런식으로 대입이가능 object를 모두 t로 바꾼다
    E passive;

    public E getPassive() {
        return passive;
    }

    public void setPassive(E passive) {
        this.passive = passive;
    }
}  

ArrayList<Tv> tvList = new ArrayList<Tv>();
이런식으로 사용자가 Tv 뿐아니라 다양한 타입을 사용 가능 하도록 한다.

지네릭스 도입 이후 자바의 많은 클래스들이 클래스<T> 식으로 변화 되었다. 결론적으로는 지네릭스 도입 이후로 불필요한 형변환, 컴파일 단계에서 타입 에러를 잡는 것이 가능해졌다.

public class ArrayList<E> extends AbstractList<E>
ArrayListclass 시작 부분인데 <E>Tv2로 바뀐다 즉 생성 할때마다 다른타입을 넣어줄수있다.
  • 지네릭스의 제약
    지네릭스 타입변수의 대입은 결국 인스턴스마다 다르게 가능하다.
    • 지네릭 타입의 배열을 생성하는 것도 허용되지 않는다. T[] tv; 이런식으로 변수의 타입을 지네릭스로 사용한는 거은 허용되지만 T[] tv = new T[]; 는 불가하다. 컴파일 단계에서 타입 T가 무엇인지 명확히 알지 못하기 때문.
    • 모든 객체에 대해 동일하게 동작해야하는 static멤버에 타입 변수 T 를 사용할 수 없다. 지네릭스의 타입은 인스턴스 변수로 간주한다. static 멤버 에서는 사용불가.
  • 제한된 지네릭 클래스, 지네릭스의 제약
    • 지네릭 클래스에 모든 타입이 아닌 지정한 타입만 오게 할 수 있는 방법이있다.
      public class FruitBox<T extends Fruit> extends Box {} 원래는 FruitBox에 모든 타입 지네릭 타입이 들어 올 수 있었지만 이와같이 Fruit과 자손들만 가능 하도록 할 수 있다.
  package JavaJungSuk3_Study.Example.ch12;

import java.util.ArrayList;

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 FruitBoxEx1 {
    public static void main(String[] args) {
        FruitBox<Fruit> fruitBox = new FruitBox<>();
        FruitBox<Apple> appleFruitBox = new FruitBox<>();
        FruitBox<Grape> grapeFruitBox = new FruitBox<>();

        Box<Fruit> fruitBox1 = new Box<Fruit>();
        Box<Apple> appletBox = new Box<Apple>();
        Box<Toy> toyBox = new Box<Toy>();

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

        appletBox.add(new Apple());
        appletBox.add(new Fruit());//  타입불일치
        appletBox.add(new Grape());//  타입불일치
        
    }

}

class FruitBox<T extends Fruit & Eatable> extends Box<T> { // 타입은 Fruit 와  Eatable을 구현,상속받은 경우만 가능
}

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

    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>
    public void set(T value){} 위클래스에 set메서드가 있을경우
    FruitBox<Fruit> fruitBox = new FruitBox<>(); fruitBox.set("멍멍"); //컴파일 에러
    set 메서드의 인자에는 Fruit타입과 자손만 허용된다.
  • 와일드 카드
    하나의 참조변수로 서로 다른 타입이 대입된 지네릭 체를 다루는 것.
    FruitBox<Fruit> fruitBox1 = new FruitBox<Fruit>();
        // FruitBox<Fruit> fruitBox2 = new FruitBox<Apple>(); // 지네릭 타입을 맞춰 줘야한다.
        FruitBox<? extends Fruit> fruitBox3 = new FruitBox<Fruit>(); // 와일드 카드를 쓰면 가능해진다 //Fruit와 그의자손들
        fruitBox3 = new FruitBox<Apple>();
        fruitBox3 = new FruitBox<Grape>();
        fruitBox3 = new FruitBox<Fruit>();

제네릭 메소드 만들기

  • 지네릭 메서드와 와일드카드와 구분하자.
    • 와일드 카드는 하나의 참조변수로 서로 다른 타입이 대입된 지네릭객체를 다루는 것.
    • 지네릭 메서드는 호출할 때마다 다른 지네릭 타입을 대입할 수 있는 것
      보통 와일드카드 메서드로 안되면 지네릭 메서드를 사용.

Erasure

  • 지네릭 타입은 컴파일 단계에서 제거된다. 가 Object로 바뀜
    제한된 경우에는 제한된 타입으로 바뀐다.
    이유는 하위호환성 때문. 결론적으로는 지네릭스는 컴파일 타임에만존재.
profile
개선하는 개발자, 이경환입니다

0개의 댓글