Generics

지윤·2021년 2월 13일
0

Java

목록 보기
18/21

컴파일시 타입을 체크해 주는 기능
컴파일 할 때 타입 체크가 가능하지만 한계가 있어서 만들어진 개념

ArrayList는 Object 배열을 가지고 있어서 모든 종류의 객체를 저장할 수 있는데, 특정 객체만 저장하고 싶다면? 특정 객체 이외의 객체가 배열에 들어오면 어떻게 잡아내야 할까? -> Generics를 사용

//숫자만 넣고 싶은 ArrayList를 생성
ArrayList list = new ArrayList();
list.add(30);
list.add(20);
list.add("30"); 

//컴파일 시 잡아낼 수 없으며
//실행하면 형변환 에러 발생 ClassCastExecption(런타임예외)
//자식끼리 즉 형제 레벨 클래스끼리는 형변환 불가능
//Object -> Integer, String
Integer i = (Integer)list.get(2); 

ArrayList<Integer> list2 = new ArrayList<Integer>();
list.add(30);
list.add(20);
list.add("30");  //컴파일 에러 발생 

장점
1. 객체의 타입 안정성을 높인다.
2. 형변환을 생략할 수 있어 코드가 간결해 진다.


추가 정리 사항
위에서 발생한 ClassCastException은 런타임 예외이다. 런타임 예외는 프로그래머 실수로 발생하는 에러로 컴파일이 아닌 실행했을 때 발생하는 예외이다. 따라서 런타임 예외를 어떻게 하면 프로그램을 실행하기 전(컴파일 타임)에 체크해볼 수 있을까?라는 생각의 결과 중 하나가 지네릭스이다. 컴파일에게 타입 정보를 제공하여 컴파일 시 체크하도록 한 것이다.
지네릭스 외에도 String 타입의 객체 초기화시 null이 아닌 " "로 초기화 하는 것도 런타임 예외인 NullPointException을 방지하기 위한 것이다.


타입 변수

  • 클래스를 작성할 때, Object 타입 대신 타입 변수를 선언해서 사용
  • 객체 생성 시 타입 변수(E) 대신 실제 타입을 지정해서 대입한다. 매개변수 호출 시 매개변수를 넣는 것과 같음
  • 실제 타입이 지정되면 형변환 생략 가능

지네릭 타입과 다형성

  • 참조변수와 생성자의 타입 변수는 일치해야 한다.
  • 지네릭 클래스간의 다형성은 성립
  • 매개변수의 다형성도 성립
List<TV> list = new ArrayList<Tv>();
List<TV> list = new LinkedList<Tv>();

ArrrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv());

제한된 지네릭 클래스

  • extends로 대입할 수 있는 타입을 제한
  • 인터페이스 경우에도 extends를 사용
//클래스2 또는 클래스2의 자손만 타입으로 지정 가능하다.
class 클래스명< T extends 클래스2> { 
    ...
}

//인터페이스와 클래스를 동시에 쓰려면 콤마가 아닌 &를 사용
class 클래스명< T extends 클래스명(클래스) & 클래스명(인터페이스)> { 
    ...
}

지네릭스의 제약

  • 타입 변수에 대입은 인스턴스 별로 다르게 가능한데
  • 따라서 static 멤버에 타입 변수 사용 불가 -> 모든 인스턴의 공통이기 때문
  • 배열, 객체를 생성할 때 타입 변수 사용 불가, 선언은 가능 -> new 연산자에 타입 변수를 사용할 수 없기 때문

와일드 카드

  • 하나의 참조 변수로 대입된 타입이 다른 여러 지네릭 객체를 참조하여 다루기 위한 것
  • 메서드의 매개변수에도 사용 가능
ArrayList<Product> list = new ArrayList<Tv>(); //타입 불일치 에러 발생

ArrayList<? extends Product> list = new ArrayList<Tv>();
ArrayList<? extends Product> list = new ArrayList<Audio>();
  • '<? extends T>': T와 그 자손들만 가능
  • '<? super T>': T와 그 조상들만 가능
  • '<?>: 제한 없음. 모든 타입 가능 = '<? extends Object>

지네릭 메서드

  • 지네릭 타입이 선언된
  • 클래스의 타입 매개변수 T와 메서드 타입 매개변수 T는 별개
  • 메서드를 호출할 때마다 다른 지네릭 타입을 대입할 수 있게 한 것
  • 와일드 카드를 사용하지 못할 때 사용하는 경우가 많음
class FruitBox<T> { //지네릭 클래스
	//지네릭 메서드
	static <T> void sort(List<T> list, Comparator<? super T> c)
}

지네릭 타입의 형변환

  • 지네릭 타입과 원시 타입 간의 형변환은 가능하지만 경고 발생
  • 서로 다른 타입이 대입된 지네릭 객체끼리 형변환 X
  • 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능하며, 생략도 가능
FruitBox<? extends Fruit> fbox = new FruitBox<Fruit>();
FruitBox<? extends Fruit> abox = new FruitBox<Apple>();
//(FruitBox<? extends Fruit>) new FruitBox<Apple>()와 같음

지네릭 타입의 제거

  • 컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣는다. (하위호환성 때문)

참고
책 자바의 정석

profile
헬로🙋‍♀️

0개의 댓글