2020-12-03
제네릭(Generic) 은 클래스 / 인터페이스 / 메서드 등의 타입을 파라미터로 사용할 수 있게 해주는 역할을 한다. 또한 비제네릭 타입의 코드에서 발생하는 불필요한 타입 변환으로 인한 프로그램 성능의 저하를 감소시킬 수 있다는 장점도 있다.
(List <Integer> list = new ArrayList <>(); 에서 많이 봤을 바로 <>가 바로 제네릭 표현식이다.)
클래스에서의 제네릭 선언 방법은 아래와 같다.
package Generic1;
public class man <T> {
private T name;
//이름 필드
private T bloodtype;
//혈액형 필드
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public T getBloodtype() {
return bloodtype;
}
public void setBloodtype(T bloodtype) {
this.bloodtype = bloodtype;
}
}
위의 코드와 같이 클래스 이름 옆에 <T>를 선언해 준 후 필드의 변수 타입을 모두 T로 선언해 주었다.
(사실 T는 변수명과 같이 제네릭의 이름일 뿐 A / B.... 등 어떤 걸로 하든 크게 상관은 없다.)
이후 타입 T는 객체를 생성할 때 해당 타입으로 변경된다.
아래는 위의 제네릭 타입으로 선언한 man 클래스의 실행코드이다.
package Generic1;
public class mainGe {
public static void main(String[] args) {
man<String> man1 = new man<>();
//새로운 객체 생성시 원하는 타입을 부여하면 된다.
man1.setName("King");
man1.setBloodtype("A");
//선언시 스트링 타입으로 선언하여 스트링 데이터를 입력하였다.
System.out.println(man1.getName());
System.out.println(man1.getBloodtype());
}
}
처음 new를 이용해 객체를 생성할 때 자신의 원하는 타입의 속성을 넣어주면 해당 속성으로 자동으로 타입이 변환된다.
코드에서는 man <String>으로 속성 값을 주어 set메서드를 이용해 String 데이터를 주고 이후 get메서드를 이용해서 데이터를 출력해 보았다.
정상적으로 String 타입의 데이터를 받아 Console 창에 출력되는 것을 확인할 수 있다.
물론 제네릭 타입 선언 시 한 가지 값이 아닌 두 가지 이상의 제네릭 타입을 받는 것도 가능하다. 구체적 예를 들기 위해 기존 man 클래스에 추가적인 T2라는 제네릭과 함께 age라는 필드를 선언해 주었다.
package Generic1;
public class man <T, T2> {
private T name;
//이름 필드
private T bloodtype;
//혈액형 필드
private T2 age;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public T getBloodtype() {
return bloodtype;
}
public void setBloodtype(T bloodtype) {
this.bloodtype = bloodtype;
}
public T2 getAge() {
return age;
}
public void setAge(T2 age) {
this.age = age;
}
}
그럼 위의 이미지처럼 기존에 선언된 man 객체에 오류가 발생하는 것을 확인할 수 있는데, 이는 새롭게 추가된 제네릭 T2의 타입을 지정해 주지 않아서이다.
새롭게 선언된 실행 코드는 아래와 같다.
package Generic1;
public class mainGe {
public static void main(String[] args) {
man<String, Integer> man1 = new man<>();
//새로운 객체 생성시 원하는 타입을 부여하면 된다.
man1.setName("King");
man1.setBloodtype("A");
//선언시 스트링 타입으로 선언하여 스트링 데이터를 입력하였다.
man1.setAge(25);
//Integer 타입으로 받아 정수값을 선언해주었다.
System.out.println(man1.getName());
System.out.println(man1.getBloodtype());
System.out.println(man1.getAge());
}
}
작성된 코드처럼 기존에 선언된 man 클래스 public class man <T, T2>의 순서에 따라 처음 T = String / T2 = Integer 속성을 부여해 주었다. 이후 Age 역시 set 메서드를 통해 25라는 데이터를 입력해주고 / Console 창에 get메서드로 출력한 결과 정상적으로 처리되는 것을 확인할 수 있다.
제네릭 메서드는 매개변수의 타입과 리턴 타입을 제네릭 타입으로 받는 메서드를 말한다. 기존의 man 클래스를 활용해 간단한 신상정보를 출력하는 메서드를 구현해보고, 구체적으로 어떻게 코드를 작성하는지 알아보자.
package Generic1;
public class Info {
public static <T,T2> void info(T t, T2 t2){
// 타입 파라미터 / 리턴타입 파라미터 or void / 메소드명 매개변수 타입
man<T, T2> man2 = new man<>();
man2.setName(t);
man2.setAge(t2);
System.out.println("나의 이름은 " + man2.getName() + " 입니다.");
System.out.println("나이는 " + man2.getAge() + " 입니다.");
}
}
Info라는 클래스를 새롭게 만들어, 제네릭 메서드를 구현하였다. 타입 파라미터는 <T, T2>로 주고, 간단한 출력 메서드이기 때문에 리턴 값은 없이 void를 설정해 주었으며, 마지막으로 매개 타입은 마찬가지로 T / T2로 설정해 주었다.
(booldtype도 같이 출력하려면, 기존에 man 클래스에 추가적인 제네릭 타입을 선언해 주어야 한다. 사실 메서드 작성하다가 깨달았다... 귀찮아서 따로 추가적인 구현을 하지 않았다.)
이제 기존의 mainGe 실행 클래스로 가서 구체적인 실행문을 작성해 보겠다.
package Generic1;
public class mainGe {
public static void main(String[] args) {
Info.info("King", 25);
}
}
정적 메서드로 선언해주어 바로 클래스명을 사용해 메서드를 호출하였다.
이후 출력문 확인 결과 정상적으로 데이터가 입력되어 메서드에 입력된 내용이 출력되는 것을 확인할 수 있다.
(글이 길어지는 것 같으니 제네릭에 대한 추가적인 내용은 다음에 정리해서 올리도록 하겠다...)