자바(JAVA) 제네릭(Generic)이란?
자바(JAVA)에서 제네릭(Generic)이란 데이터 형식에 의존하지 않으면서, 다른 데이터 타입들로 사용할 수 있는 방법이다. 클래스 내부에서 데이터 타입을 지정하는것이 아니라, 실제 클래스를 사용하는 Actor에 의해서 지정할 수 있는 것이다.
제네릭을 사용하면 실수로 지정한 타입이 들어오는 경우 컴파일 시점에서 미리 예방할 수가 있게 된다. 또한 클래스 외부에서 데이터 타입을 지정하기 때무에, 타입을 고려해서 데이터를 이리저리 변환할 필요가 없다. 코드의 재사용성이 높아지고 전체 코드 관리가 용이해진다.
제네릭(Generic)에서는 <> 안에서 데이터 타입을 자유롭게 설정할 수 있다. 특정 자료구조를 만들 때 Integer, String, Boolean 등 데이터 타입에 따라 다른 자료구조를 만든다면 굉장한 비효율이다. 자바(JAVA) 제네릭(Generic)은 이럴 때 유용하게 사용되는 문법이다.
제네릭(Generic)이 동작하는 방식은 먼저 타입의 경계를 지정하게 되면 컴파일 시점에서 해당 타입으로 데이터 타입 캐스팅이 진행되고, 매개변수의 타입을 삭제하게 된다.
제네릭(Generic)에서 많이 사용되는 문자열들은 다음과 같다. 아래 코드들은 Visual Code에서 추천할 정로 개발자들이 암묵적으로 사용하는 코드일 뿐이다. 사용자가 임의로 지정한 문자열을 넣어도 제네릭(Generic)이 동작하는데 아무런 영향을 미치지 않는다. 가장 밑의 <?>은 와일드 카드다. 즉 모든 타입을 지원한다.
타입(Type) | 의미 |
<T> | Type |
<E> | Element |
<K> | Key |
<V> | Value |
<N> | Number |
<?> | 모든 타입 가능 (= <? extends Object>) |
제네릭(Generic) 사용방법
GenericTest 클래스의 타입을 내부에서 지정하지 않고, T로만 표시해두고, Main 로직에서 String 데이터 타입으로 지정하게 된다. 즉 클래스를 인스턴스화 할 때 구체적인 데이터타입을 지정해주면 된다. 주의할 점은 제네릭(Generic)에 데이터 타입으로 들어갈 수 있는 것은 int, char같은 primitive type은 올 수가 없다. Integer, String 등 참조 타입(Reference Type)만 파라미터로 받을 수 있다.
public class Test {
// 제네릭을 사용한 interface
public interface InterfaceGenericTest<T> {}
// 제네릭을 사용한 class
static class GenericTest<T>{
}
public static void main(String[] args){
GenericTest<String> gt = new GenericTest<String>();
}
}
참조 타입을 파라미터로 받는 제네릭(Generic)은 사용자가 임의로 정의한 클래스를 데이터 타입으로 받을 수 있다. Person이란 클래스를 만들어 제네릭(Generic) 데이터 타입으로 인스턴스화를 진행한다.
public class Test {
static class GenericTest<T>{
}
static class Person{
}
public static void main(String[] args){
GenericTest<Person> gt = new GenericTest<Person>();
}
}
제네릭(Generic) 사용하기
GenericTest 클래스는 제네릭 파라미터로 <T>를 사용한다. 클래스 내부의 name 멤버의 데이터 타입도 <T>로 지정하고, getter() 메소드의 반환 데이터 타입도 <T>를 사용한다. 이제 Main 로직에서 String으로 데이터 타입을 지정하여 매개변수를 포함한 인스턴스화를 진행하면 String 타입의 name이 출력된다.
public class Test {
public interface InterfaceGenericTest<T> {}
static class GenericTest<T>{
private T name;
GenericTest(T name){
this.name = name;
}
T getter(){
return name;
}
}
static class Person{
}
public static void main(String[] args){
GenericTest<String> gt = new GenericTest<String>("Developer Blog");
System.out.println(gt.getter());
System.out.println(gt.getter().getClass().getName());
}
}
출력값을 확인해보면 name의 값과 name의 데이터 타입이 정상적으로 String으로 출력되는 것을 확인할 수 있다. 제네릭(Generic) 기능은 개발자의 입장에서 유연한 프로그램을 설계하는데 굉장히 유용한 도구다.
Developer Blog
java.lang.String
제네릭 메소드(Generic Method)
제네릭(Generic) 문법은 클래스에 의존하지 않고 메소드가 독립적으로 제네릭 타입을 지정할 수 있는 제네릭 메소드(Generic Method) 기능을 지원하고 있다. 클래스에서 지정한 제네릭 타입을 파라미터로 받아온다 하더라도 메소드에서 매개변수로 넘겨받은 데이터의 타입에 따라 독립적인 제네릭 사용이 가능한 것이다.
'접근제어자' <제네릭 파라미터> '반환타입' '메소드명'('매개변수 타입' '매개변수')
아래 예시에서는 <T> 형태의 제네릭 파라미터를 받는 GenericTest 클래스 인스턴스의 데이터 타입을 'String'으로 지정하지만, 제네릭 클래스 내부의 staticGeneric 메소드는 <E>를 제네릭 파라미터 타입으로 받아 매개변수 '10'의 데이터 타입인 Integer로 데이터 타입을 사용하고 있다.
public class Test {
public interface InterfaceGenericTest<T> {}
static class GenericTest<T>{
private T name;
GenericTest(T name){
this.name = name;
}
T getter(){
return name;
}
<E> E staticGeneric(E o){
return o;
}
}
static class Person{
}
public static void main(String[] args){
// 제네릭 클래스 인스턴스 생성
GenericTest<String> gt = new GenericTest<String>("Developer Blog");
// 제네릭 메소드 사용
System.out.println(gt.staticGeneric(100));
System.out.println(gt.staticGeneric(100).getClass().getName());
}
}
출력값을 확인해보면 Integer 타입 '100'을 매개변수로 받은 제네릭 메소드는 <E>가 Integer로 변환되었고, 반환한 데이터의 타입 또한 Integer로 출력되는 것을 확인할 수 있다.
100
java.lang.Integer
외계어 처럼 생긴 제네릭 메소드를 사용하는 이유는 뭘까? 그 이유는 정적 메소드로 선언할 때 사용되기 때문이다. 정적(static)은 클래스를 인스턴스화 하는 시점에 이미 static 메모리에 올라가게 된다. 인스턴스를 생성할 필요없이 바로 사용할 수 가 있기 때문에 클래스에 독립적인 제네릭 메소드가 사용되는 것이다. 정적 메소드로 사용되는 경우, 인스턴스 생성 이전에 메소드가 메모리에 올라가야 하기 때문에 필수적으로 클래스와 독립적인 데이터 타입을 지정해줘야 하는 것이다.
System.out.println(GenericTest.staticGeneric(10000));
제네릭(Generic) 정리
위의 예시에서 클래스 내부에서 <T>로 지정한 제네릭 타입이 인스턴스를 생성하는 시점에서 String이 되기도 하고, Integer이 되기도 했다. 또한 임의의 클래스를 만들어 참조 타입으로써 제네릭 파라미터로 사용할 수도 있었다. 모든 참조 타입은 제네릭 파라미터로 사용할 수 있었다.
제네릭(Generic)은 클래스의 데이터 타입을 지정하는 간단한 역할을 하지만 코드를 재사용하게 만들어준다는 큰 장점을 가지고 있다. 하지만 코드가 다층화되기 시작하면 어떤 타입으로 계층을 구성했는지 이해하기 어려운 코드로 변질 될 수 있다는 단점 또한 가지고 있다.
당신을 위한 콘텐츠
'Programming' 카테고리의 다른 글
스프링 부트 SpringBoot 웹 애플리케이션 개발 #5 백엔드 프론트엔드 통합 구현하기 (0) | 2022.04.02 |
---|---|
스프링 부트 SpringBoot 웹 애플리케이션 개발 #4 프론트엔드 구현하기 (0) | 2022.04.01 |
스프링 부트 SpringBoot 웹 애플리케이션 개발 #3 CRUD 구현하기 (1) | 2022.03.31 |
스프링 부트 SpringBoot 웹 애플리케이션 개발 #2 백엔드 개발 (0) | 2022.03.29 |
줌 프로그래밍 만드는 방법 클론 코딩 (0) | 2022.03.29 |
SpringBoot 웹 애플리케이션 개발 #1 프로젝트 시작 (0) | 2022.03.28 |
댓글