본문 바로가기

자바(JAVA)
[자바(Java)] 제네릭(Generics)

// Generics

- 컴파일 시 타입을 체크해 주는 기능

- 장점: 타입 안정성 제공, 형변환할 필요 없음.

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(3));
Integer i = list.get(0); //형변환 필요 x


- 여러 타입을 저장하고 싶으면 <Object> 사용 // 꺼낼 때 형변환 필요!

ArrayList<Object> list = new ArrayList<Object>();
list.add(10);
list.add("30");

Integer i = (Integer)list.get(0); // 형변환 필요 
String i = (String)list.get(1); // 형변환 필요

 

class Box<T> { }

└ Box<T> : 지네릭 클래스. 'T Box' | 'T의 Box' 라고 읽음.
└ T : 타입 변수 | 타입 매개변수.
 <> : 인자리스트
└ Box : 원시 타입

Box<String> box = new Box<String>();

└ String : 대입된 타입 (일치해야 함)
└ box : 참조변수
Box : 다형성에 의해 달라도 가능함. ex) List, ArrayList  //왼: 조상, 오: 자식

class FruitBox<T extends Fruit>{
	ArrayList<T> list = new ArrayList<T>();
    ...
}

└ 지네릭 타입에 extends를 사용하면, 특정 타입의 자손들만 대입할 수 있게 제한할 수 있음.
인터페이스를 구현해야 한다는 제약이 필요할 때도 'implements'가 아닌 'extends'를 사용함.

- 제약:
① static 멤버에 타입 변수를 사용할 수 없다.
배열 생성할 때는 타입 변수를 사용할 수 없다.(new 연산자 다음에 타입 변수 올 수 x)


package com.sorrel012.generics;

public class GerericsRunner {
	
	public static void main(String[] args) {

		MyCustomList<String> list = new MyCustomList<>();
		list.addElement("Element 1");
		list.addElement("Element 2");
		String value = list.get(0);
		
		System.out.println(list);
		System.out.println(value);
		
		System.out.println();
		
		MyCustomList<Integer> list2= new MyCustomList<>();
		list2.addElement(Integer.valueOf(5));
		list2.addElement(Integer.valueOf(7));
		Integer number= list2.get(0);
		
		System.out.println(list2);
		System.out.println(number);

	} //main

}

 

package com.sorrel012.generics;

import java.util.ArrayList;

public class MyCustomList<T> {
	
	ArrayList<T> list = new ArrayList<>();
	
	public void addElement(T element) {
		list.add(element);
	}
	
	public void removeElement(T element) {
		list.remove(element);
	}
	
	@Override
	public String toString() {
		return list.toString();
	}

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

}

package com.sorrel012.generics;

public class GenericsRunner {
	
	public static void main(String[] args) {

		MyCustomList<Long> list = new MyCustomList<>();
		list.addElement(15L);
		list.addElement(10L);
		Long value = list.get(0);
		
		System.out.println(list);
		System.out.println(value);
		
		System.out.println();
		
		MyCustomList<Integer> list2= new MyCustomList<>();
		list2.addElement(Integer.valueOf(5));
		list2.addElement(Integer.valueOf(7));
		Integer number= list2.get(0);
		
		System.out.println(list2);
		System.out.println(number);

	} //main

}

 

package com.sorrel012.generics;

import java.util.ArrayList;

public class MyCustomList<T extends Number> {
	
	ArrayList<T> list = new ArrayList<>();
	
	public void addElement(T element) {
		list.add(element);
	}
	
	public void removeElement(T element) {
		list.remove(element);
	}
	
	@Override
	public String toString() {
		return list.toString();
	}

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

}

import java.util.ArrayList;

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

public class Ex01 {
	public static void main(String[] args) {
		ArrayList<Product> productList = new ArrayList<Product>();
		ArrayList<Tv> tvList = new ArrayList<Tv>();
		
		productList.add(new Product());
		productList.add(new Tv());
		productList.add(new Audio());
		
		tvList.add(new Tv()); //tvList.add(new Product()); 불가 / tvList.add(new Audio()); 불가
		
		printAll(productList); //printAll(tvList); 불가
	} // end of main
	
	public static void printAll(ArrayList<Product> list) {
		for (Product p : list) {
			System.out.println(p);
		}
	} //end of printAll
	
} //end of class

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 Ex02 {
	public static void main(String[] args) {
		FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
		FruitBox<Apple> appleBox = new FruitBox<Apple>();
		FruitBox<Grape> grapeBox = new FruitBox<Grape>();
		//FruitBox<Toy> toyBox = new FruitBox<Toy>(); 에러!
		
		fruitBox.add(new Fruit());
		fruitBox.add(new Apple());
		fruitBox.add(new Grape());
		appleBox.add(new Apple()); //appleBox.add(new Grape()); 에러 
		grapeBox.add(new Grape());
		
		System.out.println("fruitBox = " + fruitBox);
		System.out.println("appleBox = " + appleBox);
		System.out.println("grapeBox = " + grapeBox);
	}
}

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

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(); }
}

// 와일드 카드 <?> : 하나의 참조 변수로 대입된 타입이 다른 객체를 참조할 수 있다.


- <? extends T> : 와일드 카드의 상한 제한. T와 그 자손들만 가능.

- <? super T> : 와일드 카드의 하한 제한. T와 그 조상들만 가능.

- <?> : 제한 없음. 모든 타입이 가능. <? extends Object>와 동일.

ArrayList<? extends Procduct> list = new ArrayList<Product>();
ArrayList<? extends Procduct> list = new ArrayList<Tv>();
ArrayList<? extends Procduct> list = new ArrayList<Audio>();

ArrayList<? super Audio> list = new ArrayList<Product>();
ArrayList<? super Audio> list = new ArrayList<Audio>();

import java.util.ArrayList;

class Fruit2 { public String toString() { return "Fruit"; } }
class Apple2 extends Fruit2 { public String toString() { return "Apple"; } }
class Grape2 extends Fruit2 { public String toString() { return "Grape"; } }

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

class Juicer {
	static Juice makeJuice(FruitBox2<? extends Fruit2> box) {
		String tmp = "";
		
		for(Fruit2 f : box.getList()) {
			tmp += f + " ";
		}
		return new Juice(tmp);
	}
} //end of Juicer class

public class Ex03 {
	public static void main(String[] args) {
		FruitBox2<Fruit2> fruitBox = new FruitBox2<Fruit2>();
		FruitBox2<Apple2> appleBox = new FruitBox2<Apple2>();
		
		fruitBox.add(new Apple2());
		fruitBox.add(new Grape2());
		appleBox.add(new Apple2());
		appleBox.add(new Apple2());
		
		System.out.println(Juicer.makeJuice(fruitBox));
		System.out.println(Juicer.makeJuice(appleBox));
	}
} // end of Ex03 class

class FruitBox2<T extends Fruit2> extends Box2<T> {}

class Box2<T> {
	ArrayList<T> list = new ArrayList<T>();
	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(); }	
} //end of Box2 class

<extends>

package com.sorrel012.generics;

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

public class GenericsRunner {
	
	//입력값을 입력받은 자료형, 값 그대로 돌려줌
	static <X> X doubleValue(X value) {
		return value;
	}
	
	static Number sumOfNumberList(List<? extends Number> numbers) {
		double sum= 0.0;
		for(Number number : numbers) {
			sum += number.doubleValue();
		}
		return sum;
		
	}
	
	public static void main(String[] args) {
		
		System.out.println(sumOfNumberList(List.of(1,2,3,4,5)));
		System.out.println(sumOfNumberList(List.of(1.1,2.1,3.1,4.1,5.1)));
		System.out.println(sumOfNumberList(List.of(1L,2L,3L,4L,5L)));

	} //main

}

 

package com.sorrel012.generics;

import java.util.ArrayList;

public class MyCustomList<T extends Number> {
	
	ArrayList<T> list = new ArrayList<>();
	
	public void addElement(T element) {
		list.add(element);
	}
	
	public void removeElement(T element) {
		list.remove(element);
	}
	
	@Override
	public String toString() {
		return list.toString();
	}

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

}

<super>

package com.sorrel012.generics;

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

public class GenericsRunner {
	
	//입력값을 입력받은 자료형, 값 그대로 돌려줌
	static <X> X doubleValue(X value) {
		return value;
	}
	
	static void addACoupleOfValues(List<? super Number> numbers) {
		numbers.add(1);
		numbers.add(1.0);
		numbers.add(1.0f);
		numbers.add(1L);
	}
	
	
	public static void main(String[] args) {
		
		List emptyList = new ArrayList<Number>();
		addACoupleOfValues(emptyList);
		System.out.println(emptyList);

	} //main

}

//지네릭 메서드


- 클래스 타입 매개변수<T>와 메서드의 타입 매개변수<T>는 별개

- 메서드를 호출할 때마다 타입을 대입해야 한다.(대부분 생략 가능)

- 메서드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략 불가(드문 경우)

static <T extends Fruit> Juicer makeJuice(FruitBox<T> box) {
	String tmp = "";
    for(Fruit f : box.getList()) tmp += f + " ";
    return new Juice(tmp);
}

FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();

System.out.println(Juicer.makeJuice(fruitBox)); //타입 생략 가능
System.out.println(Juicer.makeJuice(appleBox)); //타입 생략 가능

System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); //생략하지 않을경우 클래스 이름 붙이기
System.out.println(Juicer.<Apple>makeJuice(appleBox)); //생략하지 않을경우 클래스 이름 붙이기

package com.sorrel012.generics;

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

public class GerericsRunner {
	
	//입력값을 입력받은 자료형, 값 그대로 돌려줌
	static <X> X doubleValue(X value) {
		return value;
	}
	
	static <X extends List> void duplicate(X list) {
		list.addAll(list);
	}
	
	public static void main(String[] args) {

		String value1 = doubleValue(new String("하이"));
		Integer number1 = doubleValue(Integer.valueOf(5));
		ArrayList list1 = doubleValue(new ArrayList());
		System.out.println(value1);
		System.out.println(number1);
		System.out.println(list1);
		System.out.println();
		
		ArrayList numbers = new ArrayList<>(List.of(1,2,3));
		duplicate(numbers);
		System.out.println(numbers);

	} //main

}

 

package com.sorrel012.generics;

import java.util.ArrayList;

public class MyCustomList<T extends Number> {
	
	ArrayList<T> list = new ArrayList<>();
	
	public void addElement(T element) {
		list.add(element);
	}
	
	public void removeElement(T element) {
		list.remove(element);
	}
	
	@Override
	public String toString() {
		return list.toString();
	}

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

}

// 지네릭 타입의 형변환

- 지네릭 타입과 원시 타입의 형변환은 바람직하지 않다.(경고 발생)

- 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능!

Box box = null;
Box<Object> objBox = null;

box = (Box)bojBox; //지네릭 → 원시. 가능하지만 경고 발생
objBox = (Box<Object>)box; //원시 → 지네릭. 가능하지만 경고 발생

FruitBox<? extends Fruit> box = new FruitBox<Fruit>();
FruitBox<? extends Fruit> box = new FruitBoxApple>();

 

// 지네릭 타입의 제거

① 지네릭 타입의 경계를 제거


② 지네릭 타입을 제거한 후 타입이 일치하지 않으면 형변환  추가

'자바(JAVA)' 카테고리의 다른 글

[자바(Java)] 애너테이션  (0) 2022.12.17
[자바(Java)] 열거형(enum)  (0) 2022.12.17
[자바(Java)] Collections  (0) 2022.12.16
[자바(Java)] Set  (0) 2022.12.16
[자바(Java)] Comparator와 Comparable  (0) 2022.12.16