제네릭(Generics)이란?

: 특정 타입만 다루지 않고, 여러 종류의 타입으로 변신할 수 있도록 클래스나 메소드를 일반화시키는 기법입니다.

 

 

재네릭의 장점

1. 타입 안정성을 제공해줍니다.

2. 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 집니다.

 

 

- Generics 사용하기 이전에 범용적으로 사용할 수 있는 변수로 Object를 배웠습니다. Object여러 참조자료형을 사용해보도록 하겠습니다.

 

- 자료형

class FruitBox {
	Object item;
public void store(Object item)  
	{this.item =  item;}
public Object pullOut() 
    {return item;}
}

class Apple {
	int weight; // 사과의 무게
	Apple(int weight)
	    {this.weight = weight;}
	public void showAppleWeight() 
	    {System.out.println("사과의 무게 : " + weight);}
}

class Orange {
	int sugarContent; // 당분 함량
	Orange(int sugarContent)
	    {this.sugarContent= sugarContent;}
	public void  showSugarContent() 
	    {System.out.println("당분 함량 : " + sugarContent);}
}

 

public class ObjectBaseFruitBox {

	public static void main(String[] args) {
		Orange orange = new Orange(10);
		Apple apple = new Apple(500);

		FruitBox orangeBox = new FruitBox();
		orangeBox.store(orange);
		
		
		FruitBox appleBox = new FruitBox();
		appleBox .store(apple);
		
		// 1) Object 자료형을 (Apple)강제 변환
		Object orangeda = orangeBox.pullOut();
		((Orange) orangeda).showSugarContent();
		
		Object appleda= appleBox . pullOut() ;
		((Apple)appleda).showAppleWeight();
		
		
		// 2) 초기화 할때의 값을 Orange로 강제 변환해서 자장
		Orange tmpOrange = (Orange)orangeBox.pullOut();
		tmpOrange.showSugarContent();
		
		Apple tmpApple = (Apple)appleBox.pullOut();
		tmpApple.showAppleWeight();
		
		FruitBox objectBox = new FruitBox();
		
		objectBox.store("사과"); 

위의 참조자료형들을 Object를 이용하여 강제형변환을 사용하여 원하는 값을 출력할 수 있었습니다. 하지만 여기에 문제 점이 있습니다. 그것은 범용적으로 만드는 것은 좋았는데 잘못된 값을 입력하여도 error 나지 않는다는 문제점이 존재합니다. (범용적인 사용할 수 있지만 자료형에 있어서는 안전하지 않습니다.)


이러한 범용적으로 사용하면서 안정성까지 가질 수 있는 방법 = generics!

 

 Generics

: 하나의 자료형으로 범용적으로 사용고 싶을 때: new 선언시에 자료형을 결정하도록 하여 안정성을 확보합니다.  

 

class FruitBox <T>{
	T item;
public void store(T item)  
	{this.item =  item;}
public T pullOut() 
    {return item;}
}

class Apple {
	int weight; // 사과의 무게
	Apple(int weight)
	    {this.weight = weight;}
	public void showAppleWeight() 
	    {System.out.println("사과의 무게 : " + weight);}
}

class Orange {
	int sugarContent; // 당분 함량
	Orange(int sugarContent)
	    {this.sugarContent= sugarContent;}
	public void  showSugarContent() 
	    {System.out.println("당분 함량 : " + sugarContent);}
}

위의 참조자료형 정의된 것들중에서 Class FruitBox 에 Object 자리에  < T > 를 넣어서 T에 해당하는 자료형의 이름은 인스턴스를 생성하는 순간에 결정되게 합니다.

 

public class GenericBaseFruiBox {
	public static void main(String[] args) {
    
	    FruitBox<Apple> appleBox = new FruitBox<Apple> ();   
		// new 했을 떄, <T> 로 지정되었던 것이 <Apple>으로 대입해서 사용됩니다. 
		// 자료형의 type 이 apple로 고정됩니다. 
				
		Apple apple = new Apple(700);
		appleBox. store(apple);
	    // appleBox.store("사과")); // error : 자료형이 안정적으로 사용될 수 있게 되었다.  
				
		Apple tmpApple = appleBox.pullOut();
		tmpApple. showAppleWeight();
				
		FruitBox<Orange> OrangeBox = new FruitBox<Orange> ();
		Orange orange = new Orange (7);
		
        OrangeBox.store(orange);
		// OrangeBox.store(apple); //자료형이 잘못입력되어 error 
				 
	    Orange tmpOrange = OrangeBox.pullOut();
		tmpOrange.showSugarContent();
	}
}

< T > 의 자리에 참조변수 이름을 넣어서 인스턴스 생성시 결정된 T의 자료형에 일치 하지 않으면 컴파일 에러가 발생하므로 자료형에 안정적인 구조가 됩니다.

 

Generics을 통해서 코드 편의성과 자료형의 안정성 모두가 가능해졌습니다.

 

Generics method )

: 클래스의 메소드만 부분적으로 제네릭화 할 수 있습니다.

class FatherClass{
	@Override
	public String toString() {
	return "classs FatherClass"; }
}

class ChildClass{
	@Override
	public String toString() {
	return "classs ChildClass"; }
}

class InstanceTypeShower {
	int showCnt; 
	InstanceTypeShower(){
	showCnt = 0;}
	
	// GenericsMethod (자료형 1개)
	public <T>void showInstType2(T inst1) {  // 호출하는 순간 자료형으로 대신하겟다. 
		showCnt++;
		System.out.println(inst1);
	}
	
            // GenericsMethod (자료형 2개)
	public <T,U>void showInstType1(T inst1, U inst2) {  // 호출하는 순간 자료형으로 대신하겟다. 
		showCnt++;
		System.out.println(inst1);
		System.out.println(inst2);
	}
	public void showPrintCnt() {
		System.out.println("showCnt = " + showCnt);
	}
}

 

- 제네릭 메소드의 호출과정에서 전달되는 인자를 통해서 제네릭 자료형을 결정할 수 있으므로 자료형의 표현은 생략 가능 합니다.

public class IntroGenericMethod {
	public static void main(String[] args) {
		
		FatherClass father = new FatherClass();
		ChildClass child = new ChildClass();
		
		InstanceTypeShower shower = new InstanceTypeShower();
		
		shower.<FatherClass>showInstType2(father);
		shower.showInstType2(father); // <FatherClass> 생략 가능
		// shower.<FatherClass>showInstType(child); // error
		
		shower.<ChildClass>showInstType2(child);
		shower.showInstType2(child);
		
		// 2개 이상의 자료형 사용이 가능하다. 
		shower.<FatherClass,ChildClass>showInstType1(father, child);
		
		shower.showPrintCnt();
	}
}

 

매개변수의 자료형 제한)

package Generics;

interface SimpleInterface{ 
	public void showYourName();
}

class UpperClass{
	public void showYourAncestor() {
		System.out.println("UpperClass");
	}
}

class AAA extends UpperClass implements SimpleInterface{  
// 2개의 Method를 가지고 있다. 
	@Override
	public void showYourName() {
		System.out.println("Class AAA");
	}
}

class BBB implements SimpleInterface{  
	@Override
	public void showYourName() {
		System.out.println("Class BBB");
	}
}

public class BoundedTypeparam {
	public static void main(String[] args) {
		AAA aaa = new AAA();
		BBB bbb = new BBB();
		
		showInstanceAncestor(aaa); 
        // aaa 는 SimpleInterface를 구현하고 있기 때문에 문제 없다. 
		showInstanceAncestor(bbb); 
        // bbb 는 SimpleInterface를 구현하고 있기 때문에 문제 없다. 
		
		showInstanceName(aaa);
		//showInstanceName(bbb); // error extends UpperClass 하지 않기 때문이다. 
		
	}
	
	public static <T extends SimpleInterface> void showInstanceAncestor(T param) {
	// SimpleInterface 는 interface 는 implement를 사용해서 구현해야하지만 	
	// Generics 에서 상속의 개념을 넣을 때는 interface와 상속하는 class  모두 extends 사용한다. 
		
		param.showYourName();
	}
	
	public static <T extends UpperClass> void showInstanceName(T param) {
		param.showYourAncestor();	
	}
}

 

public static <T extends SimpleInterface> void showInstanceAncestor(T param) {}

 extends SimpleInterface의 자녀 class로 상속하고 있는 자료형만 가능합니다.

 

public static <T extends UpperClass> void showInstanceName(T param) {}

extends UpperClass의 자녀 class로 상속하고 있는 자료형만 가능합니다.

 

상속의 관계를 이용하여 매개변수의 자료형의 제한을 넣는 것이 가능합니다.

 

 

제네릭 메소드와 배열 )

public class IntroGenericArray {
	public static void main(String[] args) {
		String[] stArr = {"HI.", "I ' m so happy." , "JAva Generic Program."};
		showArrayData(stArr);
	}
	
	//public static <T>void showArrayData(T arr) {} 
    T 어떤 자료형이든 상관 없이 입력되는 순간 자료형으로 확정되므로  error가 나타난다. 
	
    public static <T>void showArrayData(T[] arr) {  
    // 입력 받는 참조자료형이 배열이 확신할 때, 배열 형으로 넣어 준다.                                                
		for(int i = 0; i <arr.length; i++) {
			System.out.println(arr[i]);
		}
	}
}

 

 

기본 자료형의 이름은 제네릭에 사용 불가합니다.  Wrapper 클래스를 사용하여 제네릭을 이용할 수 있습니다.

 

 

'빅데이터 > JAVA' 카테고리의 다른 글

[JAVA] Thread  (0) 2020.05.19
[JAVA] 컬렉션 프레임워크 ( Collection Framework )  (0) 2020.05.18
[JAVA] Calendar 클래스, Date 클래스 , Random 클래스  (0) 2020.05.17
[JAVA] Wrapper 클래스  (0) 2020.05.17
[JAVA] Object 클래스  (0) 2020.05.17

+ Recent posts