[Java] 제네릭을 이용하여 컬렉션의 여러 타입 파라미터를 받을수 있는 방법

제네릭(Generic)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다. 컬렉션을 사용할 때 제네릭을 특히 많이 사용한다. 하지만 파라미터로 컬렉션을 받을 때 제네릭이 Stirng이라면 String 제네릭을 가진 컬렉션만이 파라미터로 받아진다.


public class Test {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList();
        list1.add("1");
        list1.add("1");
        list1.add("1");
        list1.add("1");
        method1(list1);
 
        List<Integer> list2 = new ArrayList();
        list2.add(1);
        list2.add(1);
        list2.add(1);
        list2.add(1);
        method1(list2);  // 오류
    }
 
    public static void method1(List<String> list) {
        System.out.println(list);
    }
}
cs

method1은 그냥 컬렉션을 출력해주는 메소드이지만 파라미터 List<String> 컬렉션이 제네릭 String이 걸려있기 때문에 제네릭이 Integer인 List2는 method1에 실행시킬수 없다. 
기능은 똑같은데 단순 제네릭이 달라서 또 다른 메소드를 만들어주는건 말이 안되기 때문에 이 문제를 해결할 수 있는 방법이 있다. 제네릭에 ? 넣어주면 된다.

public class Test {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList();
        list1.add("1");
        list1.add("1");
        list1.add("1");
        list1.add("1");
        method1(list1);
        method2(list1);
 
        List<Integer> list2 = new ArrayList();
        list2.add(1);
        list2.add(1);
        list2.add(1);
        list2.add(1);
        method1(list2);  // 오류
        method2(list2);
    }
 
    public static void method1(List<String> list) {
        System.out.println(list);
    }
 
    public static void method2(List<?> list) {
        System.out.println(list);
    }
}
cs

제네릭에 ? 넣어주면 타입을 모른다고 정의한 것이다 즉 모든 타입을 받을수 있다. 하지만 이것도 불만족 스러울수 있다 모든 타입을 다 받아버리기 때문에 받지 말아야할 타입을 받아 논리적 오류를 범할수 있기 때문이다. 그래서 타입을 한정지어줄 수 있는 방법이 있다. List<? extends Object>, Map<Stirng, ? extends Object> 이런식으로 '? extends Object' 으로 제네릭을 선언하면 된다. 이 뜻은 타입은 모르는데 Object를 상속받은 모든것을 허용하겠다는 것이다.  Object는 자료형의 최상위 클래스 이므로 Integer, String 이런 기본 자료형들과 클래스들은 모두 받을 수 있는데 Object에서 벗어난 자료형들은 받지 않는 다는 것이다. 


public class Test {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList();
        list1.add("1");
        list1.add("1");
        list1.add("1");
        list1.add("1");
        method1(list1);
        method2(list1);
        method3(list1); // 오류
 
        List<Integer> list2 = new ArrayList();
        list2.add(1);
        list2.add(1);
        list2.add(1);
        list2.add(1);
        //method1(list2);  // 오류
        method2(list2);
        method3(list2);
    }
 
    public static void method1(List<String> list) {
        System.out.println(list);
    }
 
    public static void method2(List<?> list) {
        System.out.println(list);
    }
 
    public static void method3(List<extends Number> list) {
        System.out.println(list);
    }
}
 
cs

method3는 파라미터로 (List<? extends Number>)로 받고있다. 위에서 extends Object로 설명했지만  Object는 자바의 최상위 자료형이기 때문에 자바안에서는 Object를 벗어나지 못한다. 

반면 Number는 Byte, Short, Integer, Long, Float, Double 의 상위클래스이기 때문에 List<? extends Number>는 제네릭이 정수나 실수인것만 받겠다는 것이다. 그래서 제네릭이 String인 method3(list1)은 에러가 뜬다.   

댓글

Designed by JB FACTORY