JAVA

제너릭 (Generic)

황민준 2022. 9. 24. 17:31

Generic

클래스 선언 및 객체화 할 때 원하는 타입만을 넣기 위해서 쓰는 것

-> 어떤 타입이 들어 있는지 정보가 없기 때문에 확인 작업이 필요하다.

-> 제네릭을 사용하면 확인 작업을 안해도 된다.

 

package chap08.ex03.obj;

public class Box {

	private Object value; // 이 안에 뭐가 들어올지 몰라서 Object로 만들어 둔다.

	public void setValue(Object value) {
		this.value = value;
	}

	public Object getValue() {
		return value;
	}
}
package chap08.ex03.obj;

public class Main {

	public static void main(String[] args) {

		// 박스에 무엇인가 넣는다.
		Box box = new Box();
		box.setValue(123);
		
		// 박스에서 물건을 꺼낸다.
		// box에 무엇이 들었는지 정보가 없음
		// 그래서 확인 작업이 필요
		int val = (int)box.getValue();
		System.out.println(val);
	}

}

 

Generic 사용법

클래스나 인터페이스 선언 시 <T>를 붙인다.

T의 타입에 따라 데이터 타입이 변경된다.

 

package chap08.ex04.generic;

public class Box<T> { // 뭐가 들어올지 모르므로 일단 T라고 해둔다.
	
	private T value; // T 타입에 따라 변수의 데이터 타입이 변경된다.

	public T getValue() {
		return value;
	}

	public void setValue(T value) {
		this.value = value;
	}

}
package chap08.ex04.generic;

public class Main {

	public static void main(String[] args) {

		// 박스에 들어갈 물건의 종류를 명시해준다.
		Box<Integer> box = new Box<Integer>();
		box.setValue(123);
		
		// 박스를 열어본다.
		// 내용물을 알 수 있으므로 확인이 필요없다.
		int val = box.getValue();
		System.out.println(val);
	}

}

 

복수개의 제네릭을 사용 가능하다. (콤마로 구분하여)

 

package chap08.ex05.multi;

public class MultiBox<K,V> { // 두 개의 타입을 지정할 수 있다.

	private K key;
	private V value;
	
	public K getKey() {
		return key;
	}
	public void setKey(K key) {
		this.key = key;
	}
	public V getValue() {
		return value;
	}
	public void setValue(V value) {
		this.value = value;
	}
}
package chap08.ex05.multi;

public class Main {

	public static void main(String[] args) {
	
		// 첫 변수는 String 타입, 두 번째 변수는 Integer가 데이터 타입이 된다.
		MultiBox<String, Integer> box = new MultiBox<String, Integer>();
		
		//넣기
		box.setKey("홍길동");
		box.setValue(90);
		
		//꺼내기
		String name = box.getKey();
		int score = box.getValue();
		System.out.println(name+" : " + score);
		
	}

}

 

Generic의 클래스 사용

객체화 부분 제네릭에 데이터 타입을 명시하지 않아도 작동은 하지만 이를 다이아몬드 연산자라고 부른다 -> 사용은 편리하지만 효율성이 떨어진다.

 

package chap08.ex06.multi;

public class Employee<A,B,C,D,E,F,G,H> {

	private A number;
	private B name;
	private C age;
	private D money;
	private E asset;
	private F pay;
	private G marry;
	private H hobby;
	
	public A getNumber() {
		return number;
	}
	public void setNumber(A number) {
		this.number = number;
	}
	public B getName() {
		return name;
	}
	public void setName(B name) {
		this.name = name;
	}
	public C getAge() {
		return age;
	}
	public void setAge(C age) {
		this.age = age;
	}
	public D getMoney() {
		return money;
	}
	public void setMoney(D money) {
		this.money = money;
	}
	public E getAsset() {
		return asset;
	}
	public void setAsset(E asset) {
		this.asset = asset;
	}
	public F getPay() {
		return pay;
	}
	public void setPay(F pay) {
		this.pay = pay;
	}
	public G getMarry() {
		return marry;
	}
	public void setMarry(G marry) {
		this.marry = marry;
	}
	public H getHobby() {
		return hobby;
	}
	public void setHobby(H hobby) {
		this.hobby = hobby;
	}
	
}
package chap08.ex06.multi;

public class Main {

	// 제너릭 타입을 클래스로 사용하지 않은 경우
	public static void main(String[] args) {

		// 객체화 부분 제너릭에 데이터 타입을 명시해주지 않는 것을 다이아몬드 연산자라고 부른다.
		// 사용은 편리하지만 효율성이 떨어진다.
		Employee<Integer, String, Integer, Integer, Long, Float, Boolean, String> emp = new Employee<>();
		
		// 데이터 넣기
		emp.setAge(25);
		emp.setAsset((long) 50000000);
		emp.setHobby("독서");
		emp.setMarry(true);
		emp.setMoney(10000);
		emp.setName("홍길동");
		emp.setNumber(12);
		emp.setPay(1.0f);
		
		// 데이터 가져오기
		int age = emp.getAge();
		System.out.println(age);
		
	}

}

 

제네릭 부분이 길어질 경우 클래스를 사용할 수 있다.

클래스를 사용하지 않을 때 데이터를 setter/getter로 바로 꺼내올 수 있지만 코드가 길어지고 실수가 생길 수 있다는 단점이 있다.

클래스를 사용한다면 데이터를 넣고 가져오는데 한 번씩의 작업을 더 해야하지만 코드를 줄일 수 있다. (제네릭이 많을 때 유리하다.)

 

package chap08.ex07.classtype;

public class Info {

	private int emp_no;
	private String name;
	private int age;
	private int salary;
	private long assets;
	private float commision;
	private boolean married;
	private String hobby;
	
	public int getEmp_no() {
		return emp_no;
	}
	public void setEmp_no(int emp_no) {
		this.emp_no = emp_no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getSalary() {
		return salary;
	}
	public void setSalary(int salary) {
		this.salary = salary;
	}
	public long getAssets() {
		return assets;
	}
	public void setAssets(long assets) {
		this.assets = assets;
	}
	public float getCommision() {
		return commision;
	}
	public void setCommision(float commision) {
		this.commision = commision;
	}
	public boolean isMarried() {
		return married;
	}
	public void setMarried(boolean married) {
		this.married = married;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}
	
	
}
package chap08.ex07.classtype;

public class Employee <T>{

	private T t;

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}
}
package chap08.ex07.classtype;

public class Main {

	// 제너릭 타입을 클래스로 사용한 경우
	public static void main(String[] args) {

		Employee<Info> emp = new Employee<Info>();
		
		// 데이터 넣기
		Info info = new Info();
		
		info.setAge(25);
		info.setAssets(1000000);
		info.setCommision(1.0f);
		info.setEmp_no(2);
		info.setHobby("영화");
		info.setMarried(false);
		info.setName("김철수");
		info.setSalary(10000);
		
		emp.setT(info);
		
		//데이터 가져오기
		info = emp.getT();
		int age = info.getAge();
		System.out.println(age);
	}

}

 

method에서 Generic 사용

메소드에서 제네릭을 사용하면 각각 다른 매개변수 타입을 사용할 수 있다.

-> 상황에 따라 유연하게 사용 가능하다.

반환이 없는 메소드일 경우 접근제한자와 void 사이에 제네릭을 쓰고 매개변수도 제네릭 타입으로 사용한다.

반환이 있는 메소드일 경우 메소드의 반환타입도 제네릭 타입으로 적어야한다.

 

package chap08.ex08.gmethod;

public class Gmethod {

	// 제너릭을 사용하면 사용 시마다 각각 다른 매개변수 타입을 사용할 수 있다.
	public <T> void method1(T t) {
		System.out.println("입력값 : " + t);
	}
	
	// 받은 그대로 반환해 주는 경우  
	public <T> T method2(T t) {
		return t;
	}
}
package chap08.ex08.gmethod;

public class Main {

	public static void main(String[] args) {

		Gmethod gm = new Gmethod();
		gm.method1(100);
		gm.method1("감자");
		
		System.out.println(gm.method2("고구마"));
		System.out.println(gm.method2(true));
	}

}

 

Generic의 상속

클래스에서 자식의 생성자 매개변수를 받아 부모의 생성자에 전달한 것처럼 자식에서 부모에서 사용할 제네릭 타입을 받아서 전달해야 한다.

package chap08.ex09.inherit;

public class BasicInfo<N,A> {

	private N name;
	private A age;
	
	public N getName() {
		return name;
	}
	public void setName(N name) {
		this.name = name;
	}
	public A getAge() {
		return age;
	}
	public void setAge(A age) {
		this.age = age;
	}
	
	
}
package chap08.ex09.inherit;

public class DetailInfo<N, A, H> extends BasicInfo<N, A> {

	private H hobby;

	public H getHobby() {
		return hobby;
	}

	public void setHobby(H hobby) {
		this.hobby = hobby;
	}
	
}
package chap08.ex09.inherit;

public class Main {

	public static void main(String[] args) {

		DetailInfo<String, Integer, String> info = new DetailInfo<String, Integer, String>(); 
		
		info.setName("홍길동");
		info.setAge(25);
		info.setHobby("게임");
		
		String name = info.getName();
		int age = info.getAge();
		String hobby = info.getHobby();
		
		System.out.println(name + "/" + age + "/" + hobby);
	}

}