프로그래밍언어/Java

Java 제네릭(Generic) 사용 방법과 개념 정리

by 앵과장 2021. 12. 1. 09:02
반응형

 

안녕하세요
앵과장입니다.

Springboot 를 사용하기 전에는 기능에대한 공통적인 요소나 여러 데이터 타입 및 객체를 처리하기 위해서 자주 사용했는데 최근에는 자주 사용하고 있지 않다보니 어떻게 동작했었지 하고 의문을 가지게 되어 기록하고 공유 합니다.

제네릭(generic) 언제 가장 많이 사용하나요?

 

요구 조건을 개발하다보면 같은 동작과 행위를 하는데 있어서 변수에 값을 IF 조건문 그리고 Switch Case문을 사용하는데 소스가 이쁘지 않거나 모양이 좀 빠지는데 이럴경우가 존재합니다.

확장성 부분도 고려될때 사용되기도 하는데 제가 사용했던 부분은 ApiResponse 처리할때 사용했던 기억이 있습니다.

이걸 정말 많이 사용하냐 라는 질문에는 어떤 개발을 하느냐에 따라 달라지는데요 개발해야하는 비지니스 로직에 따라서 사용할수도 있고 그렇지 않을수 있습니다.

가끔 개발공부를 하다보면 정보를 습득하고 이걸 언제 써야할지 몰라서 여기저기 사용을 하게되는데 !!

익숙해지고 성숙도가 올라가시면 언제 써야하는지에 대한 선택적 판단이 성장 하실겁니다.

한마디로

쓰고 싶을때 쓰고 점진적으로 리펙토링 하면서 고도화 하시면됩니다.

제네릭은 말보다 코드로
(사실 저도 언제써야할지 감이안와요!!!)

이게 언제적 소스냐 본지 한참되었네요

제네릭을 이해하는 가장 쉬운 이해를 돕기 위한 코드입니다.

T 라는 제네릭타입이 선언 되었는데요?
Java 에서는 아래와같은 타입을 선언 후 사용하게 됩니다 자바는 타입이 있어야 합니다.

Sting, Integer, List<String>, Map<String, String>, Object

제네릭에서 사용되는 보편적인 약어에 형태이며 꼭 한글자일 필요는 없고 1개 이상이어도 상관은 없지만 코드컨벤션이 아래와 같다라는점 이해하시면 됩니다.

T type
E element
K key
V value
N number

? (wild card) 는 어떤 데이터 타입을 제공해야될지 모르겠다 라는겁니다.
즉 (? extends Object) 오브젝트를 상속하고 있는 모든 것중 하나를 말하는거라고 이해하시면 됩니다.

 

칵테일 제네릭 Class

칵테일 T 제네릭 클래스를 생성하고 

Getter, Setter 을 생성하도록 하겠습니다.

 

클래스 보시면 제네릭에서 구현할때 T를 사용합니다.

class 칵테일<T>{
	public T data;
    
    public T getData(){
    	return data;
    }
    
    public void setData(T data){
    	this.data = data;
    }
}

제네릭 문법에서 T를써도되고 위에서 명시한것처럼 용도에 맞는형태로 작성하시면됩니다.
일반적으로 사용되는것을 쓰시면 소스레벨을 이해하는데 도움이 될것 같습니다.

 

abstract class 술{
	abstract String getName();
}

class 블랙러시안 extends 술{
	private String name="블랙러시안";
    
    //자식이 부모에 메소드를 들고 있으면 override 합니다.
    public String getName(){
    	return name;
    }
    
    public void setName(String name){
    	this.name = name;
    }
}

class 준벅 extends 술{
	private String name="준벅";
    
    public String getName(){
    	return name;
    }
    
    public void setName(String name){
    	this.name = name;
    }
}

class 섹스온더비치 extends 술{
	private String name="섹스온더비치";
    
    public String getName(){
    	return name;
    }
    
    public void setName(String name){
    	this.name = name;
    }
}

3개의 칵테일 메소드를 만들고 Getter, Setter 를 이용하서 접근하는 로직을 추가하였습니다.

public class GenericEx{

	static 칵테일<? extends 술> 주문(int order){
    	if(order == 1 ){
        	System.out.println("블랙러시안을 만드는중입니다.");
            블랙러시안 koktail01 = new 블랙러시안();
            칵테일<블랙러시안> sul01 = new 칵테일<>();
            koktail01.setData(sul01);
          return sul01;
        }else if(order == 2){
        	System.out.println("준벅을 만드는중입니다.");
            준벅 koktail02 = new 준벅();
            칵테일<준벅> sul02 = new 칵테일<>();
            koktail02.setData(sul02);
          return sul02;
        }else{
        	System.out.println("섹스온더비치를 만드는중입니다.");
            섹스온더비치 koktail03 = new 섹스온더비치();
            칵테일<섹스온더비치> sul03 = new 칵테일<>();
            koktail03.setData(sul03);
          return sul03;
        }
        
    }
    
	public static void main(String[] args){
  		칵테일<? extends 술> 술01 = 주문(1);
        칵테일<? extends 술> 술02 = 주문(2);
        칵테일<? extends 술> 술03 = 주문(3);
        
        //override된 name이 호출되며 이부분을 동적바인딩이라는 표현으로 설명합니다.
        System.out.println(술01.getData().getName());
    }
}

동적 바인딩

다형성을 사용하여 메소드를 호출할때 발생하는 현상이라고 합니다.

실행 시간(Runtime) 즉, 파일을 실행하는 시점에 결정된다고 보면됩니다.

실제 참조하는 객체는 서브클래스 메소드를 호출합니다.

 

정적 바인딩

컴파일 시간에 성격이 결정된다고 합니다.

변수의 타입이나 슈퍼 클래스이니 수퍼클래스 메소드를 호출합니다.

 

위에 소스에서는 술이라는 Class를 추상클래스로 생성하고 Override할수있도록 getName또한 추상메소드입니다.

각 칵테일이 가지고 있는 고유한 이름을 호출하기위해서 사용되었다 라고 생각하면되고 술이라는 공통된 class를 상속해야

getName에 기능으로 해당 술에대한 이름을 가져올수 있습니다.

 


해당소스에 이해도는

제네릭 : 다형성, Object, 추상클래스, 오버라이드(무효화), 동적바인딩

 

제네릭을 사용하는 와일드카드 기법입니다.

해당 소스 참고하시어 좀더 좋은 방향으로 개발해보세요