• Home
  • About
    • 2daistheday's Tech Blog photo

      2dayistheday

      Today is the day.

    • Learn More
    • Email
    • Facebook
    • LinkedIn
    • Instagram
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

제네릭(Generics)

06 Sep 2017

Reading time ~5 minutes

제네릭(Generics)

: 제네릭은 ‘일반화’한다는 뜻을 담고 있다. 그리고 그 일반화의 대상은 자료형이다.

class Orange{
	int sugarContent;
	public Orange(int sugar) {
		sugarContent = sugar;
	}
	public void showSugarContent() {
		System.out.println("당도 : "+sugarContent);
	}
}

class FruitBox{
	Object item;
	public void store(Object item) {
		this.item = item;
	}
	public Object pullOut() {
		return item;
	}
}
public class Main {
	public static void main(String[] args) {
		FruitBox fBox1 = new FruitBox();
		fBox1.store(new Orange(10));
		Orange org1 = (Orange)fBox1.pullOut();
		org1.showSugarContent();
		
		FruitBox fBox2 = new FruitBox();
		fBox2.store("오렌지");
		Orange org2 = (Orange)fBox2.pullOut();
		org2.showSugarContent();
	}
}

위에서 fBox2에 오렌지를 넣고 뺄 때 당연히 오류가 나지만 컴파일에서 알 수 없다.

class Orange{
	int sugarContent;
	public Orange(int sugar) {
		sugarContent = sugar;
	}
	public void showSugarContent() {
		System.out.println("당도 : "+sugarContent);
	}
}

class OrangeBox{
	Orange item;
	public void store(Orange item) {
		this.item = item;
	}
	public Orange pullOut() {
		return item;
	}
}
public class Main {
	public static void main(String[] args) {
		OrangeBox fBox1 = new OrangeBox();
		fBox1.store(new Orange(10));
		Orange org1 = (Orange)fBox1.pullOut();
		org1.showSugarContent();
		
		OrangeBox fBox2 = new OrangeBox();
		fBox2.store("오렌지");
		Orange org2 = (Orange)fBox2.pullOut();
		org2.showSugarContent();
	}
}

위에서 처럼 Orange멤버 변수를 가진 클래스에 넣으면 컴파일에서 에러 메시지를 확인할 수 있다. 중요한 사실은 컴파일에서 에러 메시지를 확인할 수 있다는 것이다. 컴파일에서 확인할 수 있는 오류는 쉽게 해결이 가능하다. 자료에 대한 안정성이 보장된다고 볼 수 있다. Object멤버 변수를 가진 FruitBox로 클래스를 만들기보다 OrangeBox, AppleBox, … 자료형에 맞는 클래스가 안전성이 보장된다고 할 수 있다. 하지만! 각 자료형에 맞는 클래스를 만들면 같은 내용을 여럿 만드는 것이 번거롭지 않은가? 그래서~ 제네릭을 씀으로 일반화 하는 것이다~

  • 제네릭 클래스의 설계

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

    여기서 대상에 따라 변경되어야 하는 자료현은 Object

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

    이것이 바로 제네릭 클래스이다! 참고로 T는 type의 약자를 대문자로 한 것일 뿐 어떤 것이와도 상관 없다. J, H, M, V, …

    • 한 번 FruitBox를 이용하여 만들어 볼까요?

      package study_java;
      
      class Orange{
      	int sugarContent;
      	public Orange(int sugar) {
      		sugarContent = sugar;
      	}
      	public void showSugarContent() {
      		System.out.println("당도 : "+sugarContent);
      	}
      }
      
      class Apple{
      	int weight;
      	public Apple(int weight) {
      		this.weight = weight;
      	}
      	public void showWeight() {
      		System.out.println("무게 : "+	weight);
      	}
      }
      
      class FruitBox<T>{
      	T item;
      	public void store(T item) {
      		this.item = item;
      	}
      	public T pullOut() {
      		return item;
      	}
      }
      public class Main {
      	public static void main(String[] args) {
      		FruitBox<Orange> fBox1 = new FruitBox<Orange>();
      		fBox1.store(new Orange(10));
      		Orange org1 = (Orange)fBox1.pullOut();
      		org1.showSugarContent();
          		
      		FruitBox<Apple> fBox2 = new FruitBox<Apple>();
      		fBox2.store(new Apple(20));
      		Apple app = (Apple)fBox2.pullOut();
      		app.showWeight();
      	}
      }
      

      ​

  • 제네릭 메소드의 정의와 호출

    class AAA{
    	public String toString() {
    		return "Class AAA";
    	}
    }
    
    class BBB{
    	public String toString() {
    		return "Class BBB";
    	}
    }
    
    class InstanceTypeShower
    {
    	int showCnt = 0;
    	public <T> void showInstType(T inst) {
    		System.out.println(inst);
    		showCnt++;
    	}
    	void showPrintCnt() {
    		System.out.println("Show count: "+showCnt);
    	}
    }
    public class Main {
    	public static void main(String[] args) {
    		AAA aaa = new AAA();
    		BBB bbb = new BBB();
      		
    		InstanceTypeShower shower = new InstanceTypeShower();
    		shower.<AAA>showInstType(aaa);
    		shower.<BBB>showInstType(bbb);
    		shower.showPrintCnt();
    	}
    }
    

    제네릭 메소드의 호출방법을 익히면 됩니다. AAA를 통해 T를 AAA클래스로 인식한다. 하지만 를 지운다고 못알아볼까? 자동으로 참조변수 aaa를 근거로 AAA클래스를 연결한다.

    또한 둘 이상의 자료형 매개변수를 선언하고 각각에 다른 자료형 정보를 전달할 수 있다.

    class InstanceTypeShower2
    {
    	public <T, U> void showInstType(T inst1, U inst2) {
    		System.out.println(inst1);
    		System.out.println(inst2);
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		AAA aaa = new AAA();
    		BBB bbb = new BBB();
      		
    		InstanceTypeShower2 shower = new InstanceTypeShowe2();
    		shower.<AAA, BBB>showInstType(aaa, bbb);
    	}
    }
    
  • 매개변수로 이 자료형만 전달하세요.

    package study_java;
    
    interface SimpleInterface{
    	public void showYourName();
    }
    
    class UpperClass{
    	public void showYourAncestor() {
    		System.out.println("UpperClass");
    	}
    }
    
    class AAA extends UpperClass implements SimpleInterface{
    	public void showYourName() {
    		System.out.println("Class AAA");
    	}
    }
    
    class BBB extends UpperClass implements SimpleInterface{
    	public void showYourName() {
    		System.out.println("Class BBB");
    	}
    }
    
    class Main{
    	public static<T extends SimpleInterface> void showInstanceAncestor(T param) {
    		param.showYourName();
    	}
    	public static <T extends UpperClass> void showInstanceName(T param) {
    		param.showYourAncestor();
    	}
      	
    	public static void main(String[] args) {
    		AAA aaa = new AAA();
    		BBB bbb = new BBB();
      		
    		showInstanceAncestor(aaa);
    		showInstanceName(aaa);
    		showInstanceAncestor(bbb);
    		showInstanceName(bbb);
    	}
    }
    
  • 와일드카드와 제네릭 변수의 선언
    와일드 카드란 이름 또는 문자열에 제한을 가하지 않음을 명시하는 용도로 사용되는 특별한 기호다.
    class Fruit{
    	public void showYou() {
    		System.out.println("나는 과일입니다.");
    	}
    }
    class Apple extends Fruit{
    	public void showYou() {
    		super.showYou();
    		System.out.println("나는사과입니다.");
    	}
    }
    
    class FruitBox<T>{
    	T item;
    	public void store(T item) {
    		this.item = item;
    	}
    	public T pullOut() {
    		return item;
    	}
    }
    
    class Main{
    	public static void openAndShowFruitBox(FruitBox<? extends Fruit> box) {
    		Fruit fruit = box.pullOut();
    		fruit.showYou();
    	}
      	
    	public static void main(String[] args) {
    		FruitBox<Fruit> box1 = new FruitBox<Fruit>();
    		box1.store(new Fruit());
      		
    		FruitBox<Apple> box2 = new FruitBox<Apple>();
    		box2.store(new Apple());
      		
    		openAndShowFruitBox(box1);
    		openAndShowFruitBox(box2);
    	}
    }
    
    • 자료형에 상관없이 참조변수 선언 와일드카드
      FruitBox<?> box
    • 하위 클래스를 제한하는 용도의 와일드카드
      FruitBox<? super Apple> boundedBox —> 위의 경우 Object, Fruit, Apple 자료형 허용
  • 제네렉 클래스 상속

    class AAA<T>{
      T itemAAA;
    }
    
    class BBB<T> extends AAA<T>
    {
      T itemBBB;
    }
    
    OR
    
    class BBB extends AAA<String>{
      int itemBBB;
    }
    
    OR
    
    class BBB<T> extends AAA<String>{
      T itemBBB;
    }
    
  • 제네릭 인터페이스 구현

    interface MyInterface<T>
    {
      public T myFunc(T item);
    }
    
    class MyImplement<T> implements MyInterface<T>
    {
      public T myFunc(T item){
        return item;
      }
    }
    
    OR
    
    class MyImplement implements MyInterface<String>
    {
      public String myFunc(String item){
        return item;
      }
    }
    

    ​

  • 기본 자료형의 이름은 제네릭 클래스의 인스턴스 생성에 사용될 수 없습니다.

    FruitBox<int> fb1 = new FruitBox<int>();
    

    안돼요.

    FruitBox<Integer> fb1 = new FruitBox<Integer>();
    

    돼요.



JAVA제네릭class Share Tweet +1