자바/자바 입문 공부일지

자바 기초 공부 일지 36. 제네릭generic 타입 인자

Tomitom 2022. 11. 2. 12:00
반응형

제네릭 타입은 클래스나 인터페이스 뒤에 <> 다이아몬드 부호를 넣고, 타입 변수가 들어가는 것입니다. 

class<T>, interface<T> 일반적으로 대문자 하나가 들어갑니다. 

 

● 제네릭은 그 자체로 자료형이기 때문에 매개변수화 타입을 타입 인자로 전달할 수 있습니다. 

즉 상자 속에 상자를 넣을 수 있습니다. 마트료시카처럼요. 

 

 

class Box<T> {  // 박스 T를 만듭니다. 

   private T ob;

   public void set(T o) {  // 인자를 가질 수 있는 셋팅 메소드를 만들고 

      ob = o;

   }

   public T get() {  // 인자를 반환하는 반환 메소드를 만들어요. 

      return ob;

   }

}

 

public static void main(String[] args) {

   Box<String> sBox = new Box<>();  // 문자열을 담는 박스를 만듭니다. 

   sBox.set("I am so happy.");  // 박스 안에 ' 나는 행복합니다' 문자를 넣어요. 

  

   Box<Box<String>wBox = new Box<>();  // 문자열을 넣은 박스를 다른 박스 안에 타입 인자로 넣습니다. 

   wBox.set(sBox);

   Box<Box<Box<String>>zBox = new Box<>(); // 문자열을 넣은 박스를 넣은 박스를 다른 박스 안에 넣습니다... 

   zBox.set(wBox);

   System.out.println(zBox.get().get().get());  // 꺼내고 꺼내고 꺼내면 문자열이 나옵니다.

}

 

 

● 제네릭 클래스의 타입 인자 제한 

class Box<T extends Number> {...}

   → 인스턴스 생성 시 타입 인자로는 Number 또는 이를 상속하는 클래스만 올 수 있습니다. 

Number와 Number의 자식들만 올 수 있습니다. = 제네릭의 타입 인자 상한제한. 

 

public static void peekBox(Box<? extends Number> box) {
   System.out.println(box);
}

box는 Box<T> 인스턴스의 참조 값을 전달받는 매개변수입니다. 
  → 단 전달되는 인스턴스의 T는 Number 또는 이를 상속하는 하위 클래스이어야 합니다. 

 

public static void peekBox(Box<? super Integer> box) {
   System.out.println(box);
}

box는 Box<T> 인스턴스의 참조 값을 전달받는 매개변수입니다.
 → 단 전달되는 인스턴스의 T는 Integer 또는 Integer가 상속하는 클래스이어야 합니다. 
   즉 위 메소드의 인자로 전달 가능한 인스턴스는 Box<Integer>, Box<Number>, Box<Object>으로 제한됩니다. 

 


class Box<T extends Number> {

   private T ob;
   public void set(T o) {
      ob = o;
   }
   public T get() {
      return ob;
   }
}

public static void main(String[] args) {

   Box<IntegeriBox = new Box<>();
   iBox.set(24);
   Box<DoubledBox = new Box<>();
   dBox.set(5.97);
   . . . .
}
<오류인 예> 

class Box<T> {

   private T ob;
   ....
   public int toIntValue() {
      return ob.intValue();   // ERROR!
   }
}
<오류가 나지 않는 예> 

class Box<T extends 
Number> {
   private T ob;
   ....
   public int toIntValue() {
      return ob.intValue();   // OK!
   }
}

사이 배운 것을 바탕으로 다중 매개변수를 받는 제네릭으로 

제네릭을 타입 인자로 넣는 코드를 작성해볼게요. 

package day18;


class DBox<L, R>{		//L과 R을 받는 제네릭 박스를 만듭니다. 
	private L left; 
	private R right; 

	void set(L left, R right) {
		this.left = left; 
		this.right = right; 
	}
	
}
public class Generic01 {

	public static void main(String[] args) {

		DBox<String, Integer> box = new DBox<>(); // 각각 문자열과 정수형을 받는 box 인스턴스를 생성
		box.set("백백백", 100);

		
		DBox<String, DBox<String, Integer>> bbox = new DBox<>(); // box 인스턴스를 매개변수로 받는 bbox 생성
		bbox.set("또, 문자열", box);
		
	}

}

설명은 주석을 참고해주세요!

 

그럼 이것을 바탕으로 제네릭 클래스의 타입 인자를 인터페이스로 제한하는 것을 한 번 더 구현해볼게요. 

 

interface Eatable {     // '먹을 수 있는' 인터페이스 클래스를 생성합니다.
   public String eat();     // 그 안의 추상 메소드는 문자열 eat()을 반환하는 메소드입니다. 이것을 정의해주어야만 이 인터페이스를 완성할 수 있습니다. 
}

class Apple implements Eatable {    // '먹을 수 있는' 클래스를 상속한 사과 클래스를 만들고 
    public String toString() {
        return "I am an apple.";        //  '나는 사과야'를 출력하는 문자열을 추가합니다. 
    }
    
    @Override       // implements 는 반드시 메소드를 부모의 메소드를 오버라이딩 해야하므로 
    public String eat() {       // eat() 이라는 추상 메소드에 
        return "It tastes so good!";    // 무척 맛있지! 라는 문자열을 오버라이딩 합니다. 
    }
}

class Box<T extends Eatable> {      // '먹을 수 있는' 클래스를 상속하는 박스 제너럴 클래스를 생성합니다. 
    private T ob;      

    public void set(T o) {
        ob = o;
    }

    public T get() {       //get() 제너럴 메소드를
        System.out.println(ob.eat());    // Eatable 인터페이스로 제한하였기에  eat()함수의 호출이 가능합니다. 
        return ob;      // 즉 여기에서 It tastes so good! 이 출력됩니다. 
    }    
}

class BoundedInterfaceBox {     
    public static void main(String[] args) {
        Box<Apple> box = new Box<>();       // 박스 제네럴 클래스에 나는 사과라고 주장하는 Apple 클래스를 담아 새로운 인스턴스를 만듭니다. 
        box.set(new Apple());       // 제너럴 set의 자리에 사과를 넣습니다. 
        
        Apple ap = box.get();        // Apple 클래스의 인스턴스 ap를 겟 박스 안에 넣어줍니다. 
        System.out.println(ap);     // ap클래스를 출력합니다. 여기에서 I am an apple.이 출력 됩니다. 
    }
}

 

 

반응형