개발하는 삶

[Java] 제네릭스 본문

Java

[Java] 제네릭스

삶_ 2022. 6. 25. 17:32

제네릭스

  • 컴파일 시 타입을 체크해주는 기능
    • 잘못된 타입이 사용되는 문제를 줄여줌
    • 후에 형변환 할 필요없음. 코드가 간결해진다.
    • 타입을 <> 로 가지는 클래스, 인터페이스
      • public class 클래스명<T>{...}
      • public interface 인터페이스명<T>{...}
  • ArrayList<Object> list = new ArrayList<Object>();
    • list에 Object 타입만 넣을 것이라는 뜻
import java.util.ArrayList;

public class GenericTest {
	public static void main(String[] args) {
		
		//ArrayList list = new Arraylist(); //~jdk1.5(지네릭스 도입이전)
		ArrayList<Object> list = new ArrayList<Object>(); //지네릭스 도입이후, 미리 타입체크
		list.add(10);
		list.add("30"); //문자열 타입

		String i = (String)list.get(2);
	}
}

 

타입 변수

  • 아무 이름으로 저장해도 컴파일에 문제가 없음
  • 클래스를 작성할 때, Object타입 대신 타입변수(E)를 선언해서 사용
    • 일반클래스 → 지네릭 클래스
public class ArrayList<E> extends AbstractList<E> {
	private transient E[] elementData;
	public boolean add(E o) {/..}
	public E get(int index) {/..}
	...
}
  • 객체를 생성 시, 타입변수(E) 대신 실제 타입(ex. Tv)를 대입
public class ArrayList extends AbstractList {
	private transient Tv[] elementData;
	public boolean add(Tv o) {/..}
	public Tv get(int index) {/..}
	...
}
  • 타입변수 대신 실제 타입이 지정되면, 형변환 생략 가능
//before

ArrayList tvList = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv)tvList.get(0); //형변환O 필수

//after

ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
Tv t = tvList.get(0); //형변환X

 

지네릭스 용어

  • Box<T> : 지네릭 클래스
    • T : 타입변수 / 타입매개변수
    • Box : 원시타입 (기존의 원래 타입)
    • 지네릭 클래스 선언 : class Box<T> {}
  • Box <String> b = new Box<String>();
    • String = 대입된 타입

 

지네릭 타입과 다형성

  • 참조변수 == 생성자의 대입된 타입
    • 조상-자손 관계여도 안됨 (대입된 타입이 달라서)
     
class Tv extends Product {}
//일때,
ArrayList<Product> list = new ArrayList<Tv>(); //에러

 

지네릭 클래스간의 다형성은 성립 (대입된 타입은 일치해야함)

// ArrayList= 자손, List= 조상
List<Tv> list = new ArrayList<Tv>();

// LinkedList= 자손, List= 조상
List<Tv> list = new LinkedList<Tv>();

 

매개변수의 다형성도 성립

class Tv extends Product {}
//일때,

ArrayList<Product> list = newArrayList<Product>();
list.add(new Product());
list.add(new Tv()); //Product 자손도 OK
list.add(new Audio()); //Product 자손도 OK

 

 

Iterator<E>

  • 클래스 작성시, Object타입 대신 타입변수를 사용
Iterator<Student> it = list.iterator();
while(it.hasNext()) {
	Student s = it.next(); //형변환 필요X
	...
}

 

HashMap<K,V>

  • 여러 개의 타입변수가 필요할 때
    • HashMap<key값,Value값> 예시로 들 때,
class Ex2 {
	public static void main(String[] args) {

		HashMap<String,Student> map = new HashMap<String, Student>(); //생성
		//<>값이 일치하면 JDK1.7부터 타입지정 생략가능
		//HashMap<String,Student> map = new HashMap<>(); //와 같음
		map.put("자바왕", new Student("자바왕",1,1,100,100,100)); //데이터 저장

		Student s = map.get("자바왕");

		System.out.println(map);
	}
}

class Student {

	String name = "";
	int ban;
	int no;
	int kor;
	int eng;
	int math;

....
}

 

제한된 지네릭 클래스

  • extends로 대입할 수 있는 타입을 제한
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의 자손이 아님
  • 인터페이스인 경우에도 extends를 사용 (implements 아님!)
    • 두가지 묶을때는 &로 묶기
    • ex. T extends Fruit & Eatable
interface Eatable{}
class FruitBox<T extends Eatable> {...}

 

 

지네릭스의 제약

  • 타입 변수에 대입은 인스턴스별로 다르게 가능
    • Box<Apple> appleBox = new Box<Apple>(); //appleBox인스턴스 -> Apple객체만 저장가능
    • Box<Grape> grapeBox = new Box<Grape>(); //grapeBox인스턴스 -> Grape객체만 저장가능
  • static 멤버에 타입변수 사용불가
    • class Box<T> {
    • static T item; //에러
    • static int compare(T t1, T t2){...}; //에러
    • ... }
  • 배열 생성시, 타입변수 사용 불가.  (선언만 가능)
    • class Box<T> {
    • T[] itemArr; //배열 선언은 OK
    • ...
    • T[] toArray() {
    • T[] tmpArr = new T(itemArr_length); //에러
    • //new T 이런 형식의 배열 생성이 안됨! new 연산자 안됨.
    • ... }

 

 

와일드 카드 <?>

  • 하나의 참조변수로 대입된 타입이 다른객체를 참조 가능
  • ArrayList<? extends Product> list = new ArrayList<Tv>();
  • ArrayList<? extends Product> list = new ArrayList<Audio>();
  • ArrayList<Product> list = new ArrayList<Tv>(); //에러. 대입된 타입이 불일치
  • <?> 사용법
    • 지네릭스 활용 안되는것에 보통 사용됨
    • <? extends T> : T와 그 자손들만 가능
    • <? super T> : T와 그 조상들만 가능
    • <?> : 제한 없음. 모든타입이 가능 == <? extends Object>
  • 메서드의 매개변수에 와일드카드를 사용
apple<?> a = new apple<?>(); //에러. 어떤 타입인지 전혀 알수없음
apple<?> a = new apple<object>(); //됨
apple<?> a = new apple<>(); //됨. 위 문장과 같은 문장임 (타입유추가 가능해서)

 

 

지네릭 메서드

  • 지네릭 타입이 선언된 메서드
//제네릭 메서드 선언하기
public <T> 리턴타입 메서드명(매개변수...){..}

//리턴타입으로 제네릭타입 사용하기 : Box<T>
public <T> Box<T> boxing(T t){...}

 

  • 지네릭 메서드 호출하는 법
//리턴타입 변수 = <구체적타입> 메서드명(매개값);
Box<Integer> box = <Integer>boxing(100);

//리턴타입 변수 = 메서드명(매개값);
//매개값을 보고 <구체적타입>을 추정
Box<Integer> box = boxing(100);

 

 

지네릭 타입의 제거

  • 컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣는다
    • 지네릭 타입의 경계를 제거
    • 하위호완성을 위해 (JDK 1.5 이전버전과 문제없이 사용할수 있게)
class Box<T extends Fruit> {
	void add(T t) {
		...
	}
}

//를, 컴파일 후에

class Box {
	void add(Fruit t) { // T -> Object -> Fruit
		...
	}
}

//로 바꿔짐

 

지네릭 타입 제거 후, 타입이 불일치하면 형변환을 추가

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

//를, 컴파일 후에 타입이 불일치하면

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

//형변환을 추가

 

 

 

'Java' 카테고리의 다른 글

[Java] Stream  (0) 2022.06.27
[Java] 람다식  (0) 2022.06.26
[Java] 예외, 래퍼 클래스  (0) 2022.06.25
[Java] 스레드  (0) 2022.06.21
[Java] Object, String 클래스  (0) 2022.06.21