JAVA

[Java.5-2] 제네릭 메소드와 와일드 카드

식빵민 2022. 5. 20. 21:00

Generic Method : 타입 매개변수를 사용하는 메소드

  •  제네릭 클래스 뿐 아니라 일반 클래스의 멤버도 될 수 있다.
  • 제네릭 메소드를 정의할 때에는 타입 매개변수를 반환 타입 앞에 위치시킨다.
    리턴 타입 앞에 <> 기호를 추가한다.
    제네릭 메소드는 매개 타입 또는 리턴 타입으로 타입 매개변수를 사용한다.(매개변수 or 리턴타입 일반화)
class MyArray{
    static <T> void swap(T[]arr, int i, int j){
    	T tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}
  • 제네릭 메소드를 호출할 때에는 구체적인 타입을 생략할 수 있다.
Integer []arr = {1,2,3,4,5};
MyArray.swap(arr,0,4);

한정된 타입 매개변수

선언부의 타입 매개변수에 대해 특정 타입으로 제한하고 싶은 경우 -> 타입 한정 가능

  • extends 클래스명(or 인터페이스명)
    인터페이스인 경우에도 extends 사용
  • T가 제한되므로, T에서 접근 가능한 메소드의 종류는 한정됨
    ex. Number 클래스(추상클래스) -> Wrapper클래스의 슈퍼클래스, 8개의 기본타입에 대응되는 클래스 
class MyMath{
    static <T extends Number> double getAverage (T[]arr){
    	double a = 0;
        for(int i =0;i<arr.length;i++){
        	a += arr[i].doubleValue();
        }
        a = a/arr.length;
        return a;
    }
}
static<T> double getAverage(T[]arr){
	arr[].doubleValue();//!!오류!!
    	//어떤 타입의 배열이 들어올지 모르기 때문에 접근 가능한 메소드에 제한이 있음
}

제네릭 클래스 객체 생성과 전달

class MyClass<T extends Number>{
    T val;
    void set(T a){val = a;}
    T get(){return val;} 
}
MyClass<Integer>n1 = new MyClass<Integer>();
MyClass<Double>n2 = new MyClass<Double>();
n1.set(3);
n2.set(3.3);

printDouble(n1);
printDouble(n2);
public class GenericTest{
    static void printDouble(MyClass<Number>n){//!오류! -> 와일드 카드 사용해야함!
    	String data = n.get().doubleValue();
        System.out.print(data);
    }
}

와일드 카드 : 하나의 참조변수로 여러 다른 타입변수를 가진 제네릭 클래스 객체를 참조할 수 있는 방법

 

  • Unbounded wildcards : <?>
    <?> 사용
    ? 자리에는 아무 타입이나 들어갈 수 있다.
MyClass<?> n;
n = new MyClass<Double>();
n = new MyClass<Integer>();
n = new MyClass<String>();
public class GenericTest{
    static void printNumber(MyClass<?>n){
    	String data = n.get().doubleValue();
        System.out.print(data);
    }
}
  • Upper bounded wildcard : extends 키워드를 사용하여 슈퍼클래스를 명시한다.
    ? extends ~
    ?의 슈퍼클래스에 ~가 있어야함
MyClass<? extends Number> n;
n = new MyClass<Double>();
n = new MyClass<Integer>();
n = new MyClass<String>();
// Number 클래스는 Double, Integer, String 클래스의 슈퍼클래스
  • Lower bounded wildcard : super 키워드를 사용하여 서브클래스를 명시한다.
    ? super ~
    ?의 서브클래스에 ~가 있어야함
MyClass<? super Number> n;
n = new MyClass<Object>(); // Object자리에 Wrapper 클래스 못옴
// Number클래스는 Object 클래스의 서브클래스

와일드 카드와 제네릭 메소드의 차이

//Wildcard
static double max1(MyClass <? extends Number>n1, MyClass <? extends Number>n2){
    double dn1 = n1.get()doubleValue();
    double dn2 = n2.get()doubleValue();
    if(dn1 > dn2) return dn1;
    else return dn2;
}
//Generic
static double max2(MyClass <T>n1, MyClass <T>n2){
    double dn1 = n1.get()doubleValue();
    double dn2 = n2.get()doubleValue();
    if(dn1 > dn2) return dn1;
    else return dn2;
}
MyClass<Integer>n1 = new MyClass<Integer>();
MyClass<Integer>n2 = new MyClass<Integer>();
n1.set(10);
n2.set(11.1);
System.out.println(max1(n1,n2));// !성공! 
 // n1, n2 가 Integer, Double 로 각각 달라도 상관없음
System.out.println(max2(n1,n2));// !오류!
 // n1, n2 가 타입이 같아야됨

제네릭 주의사항 

  • 제네릭 타입변수로 객체 생성이 불가하다.
    제네릭 타입변수 레퍼런스는 선언 가능
T a; // 가능
a = new T();// 불가능
T a = new Integer(10);// 가능
  • 제네릭 타입변수로 배열 생성 불가
    선언은 가능
    매개변수로의 제네릭 타입 배열은 사용 가능
T[] a; // 가능
T[] a = new T[10]; // 불가능
T[] a = new Integer[10]; // 가능


public void myArray(T[] a){...}// 가능