타입 파라미터(매개변수)로 제네릭을 사용하는 경우, 외부에서 대입되는 타입 인자의 형 변환(캐스팅)문제가 발생하기 때문
제네릭을 지정하면, 지정한 타입 파라미터만 사용하도록 제한할 수 있는데
만일 메서드의 파라미터 타입을 Object으로 설정하여 사용하려고 할 때, 외부에서 대입되는 타입 인자가 Object가 아닌 경우(예를 들어 String, Integer타입이면)
제네릭은 타입 파라미터가 완전히 똑같은 타입만 받기 때문에 업캐스팅이 되지 않음
즉, 제네릭에서는 다형성이 적용되지가 않아서 매개변수를 상위 클래스인 Object 타입으로 정하더라도, 제네릭이 String(타입 인자가 String)으로 정해져 있다면
타입 인자가 Object타입이 아니므로, String이 Object 타입으로 업캐스팅 되지 않음
→ 이를 해결하기 위해 나온 기능이 제네릭 와일드 카드임
제네릭 타입 매개변수끼리 형 변환을 할 수 없어서 발생하는 문제점 확인하기
1. 배열을 이용할 때
메서드의 파라미터 타입이 Object(상위 클래스)이고, 외부에서 대입되는 타입 인자인 Integer(하위 클래스)인 경우, 업캐스팅이 됨
즉, Integer(하위 클래스)가 Object(상위 클래스)로 업캐스팅이 됨
public class Main {
static void getArray(Object[] arr) {
for (Object obj : arr) {
System.out.println(obj);
}
}
public static void main(String[] args) {
Integer [] integers = {1, 2, 3};
getArray(integers); // [1, 2, 3]
}
}
Integer[ ] 배열 타입이 Object[ ] 배열 타입으로 업캐스팅이 되어 사용 가능
2. List의 제네릭에 타입 파라미터를 지정하여 넘기기
타입 인자가 Integer이고 받는 매개변수의 타입이 Object일 때, 메서드 호출 부분에서 컴파일 에러 발생
제네릭으로 지정하면, 상위 클래스(Object)와 하위 클래스(Integer) 사이에 업캐스팅(형 변환)이 되지 않음
List의 제네릭의 경우, 받는 매개변수의 타입을 Object로 설정 하였고, 외부에서 대입되는 타입 인자가 Integer인 경우에
일반 메서드처럼 Integer가 Object로 업캐스팅 되는 것이 아님
즉, Integer 타입이 Object 타입(상위 클래스)으로 업캐스팅 되지 않음
이유는 타입 인자와 타입 파라미터가 똑같은 타입만 받을 수 있기 때문에
Integer타입이 Object 타입(상위 클래스)으로 업캐스팅 되지 않음
제네릭 와일드카드
와일드 카드
? : 모든 클래스 타입이나 인터페이스 타입이 적용될 수 있음
사실 <?>만 사용하면 Object(상위 클래스)와 의미가 다르지 않으므로, 보통 제네릭의 타입을 한정해주는 키워드(extends) 등과 함께 사용함
제네릭 타입을 한정해주는 키워드와 와일드카드를 함께 사용하기
상속 관계(상하 관계)가 있는 클래스들의 제네릭 적용 범위를 제한해줄 수 있음
와일드카드
설명
<?>
모든 참조 타입이 가능함
<? extends A>
상위 클래스를 제한
A와 그하위 클래스만 타입으로 가능
<? super A>
하위 클래스를 제한
A와 그상위 클래스만 타입으로 가능
와일드 카드 사용하기
1. <?> : 어떤 참조 타입도 저장 가능
List <?> : List 자료형에 어떤 참조 타입이든지 저장할 수 있음
2. extends : 타입의 상한을 제한하기
<? extends A> 받을 수 있는 타입의 상한을 제한
상속관계에 있는 경우에, 제한 타입은 A(부모 클래스)가 상한이고, 하위 클래스만 받을 수 있음
class Person {
String name;
public Person(){}
public Person(String name) {
this.name = name;
}
}
class Employee extends Person {
int salary;
public Employee() {}
public Employee(String name, int salary) {
super.name = name;
this.salary = salary;
}
}
class Student extends Person{
int grade;
public Student(String name, int grade) {
super.name = name;
this.grade = grade;
}
}
public class Main {
public static void main(String[] args) {
// List에 저장되는 데이터는 Person 객체
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("정공자"));
personList.add(new Person("비정공"));
personList.add(new Person("컴공자"));
// List에 저장되는 데이터는 Employee 객체
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(new Employee("정공자", 10000));
employeeList.add(new Employee("비정공", 20000));
employeeList.add(new Employee("컴공자", 30000));
// List에 저장되는 데이터는 Student 객체
List<Student> studentList = new ArrayList<Student>();
studentList.add(new Student("정공자", 1));
studentList.add(new Student("비정공", 2));
studentList.add(new Student("컴공자", 3));
// List<?> : List에 어떤 객체를 담고 있어도 전달 가능
printPersonList(personList);
printPersonList(employeeList);
printPersonList(studentList);
// List<? extends Employee> : List에 Employee, 이하 하위클래스만 담을 수 있음
printEmployeeList(employeeList);
// printEmployeeList(personList); 상위 클래스는 불가
// List<? extends Student> : List에 Student, 이하 하위클래스만 담을 수 있음
printStudentList(studentList);
// printStudentList(personList); 상위 클래스는 불가
}
}
[ 예제 ] List<? extends Number>
List에 저장되는 타입의 상한을 제한해줌
Number (상위 클래스) > Integer, Double (하위 클래스)
Number가 사용할 수 있는 타입의 상한이고, 이하 하위클래스인 Integer, Double 등만 사용 가능
3. super : 타입의 하한을 제한하기
<? super A> 받을 수 있는 타입의 하한을 제한
상속관계에 있는 경우에, 제한 타입은A(자식 클래스)가 하한이고, 상위 클래스만 받을 수 있음
public class Main {
// 모든 클래스 객체가 저장된 List를 전달
// ? : Person, Student, Employee 객체가 저장된 List 모두 전달 가능
public static void printPersonList(List<?> list) {
for (Object obj : list) {
System.out.println( ((Person)obj).getName() );
}
}
// Employee클래스와 상위 클래스 타입이 저장된 List를 전달 가능
public static void printEmployeeList(List<? super Employee> list) {
for (Object obj : list) { // 어떤 타입일지 몰라서 Object(상위클래스)로 받음
System.out.println( ((Employee)obj).getSalary() );
}
}
// Student클래스와 상위 클래스 타입이 저장된 List를 전달 가능
public static void printStudentList(List<? super Student> list) {
for (Object obj : list) { // 어떤 타입일지 몰라서 Object(상위클래스)로 받음
System.out.println( ((Student)obj).getGrade() );
}
}
public static void main(String[] args) {
// List에 저장되는 데이터는 Person 객체
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("정공자"));
personList.add(new Person("비정공"));
personList.add(new Person("컴공자"));
// List에 저장되는 데이터는 Employee 객체
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(new Employee("정공자", 10000));
employeeList.add(new Employee("비정공", 20000));
employeeList.add(new Employee("컴공자", 30000));
// List에 저장되는 데이터는 Student 객체
List<Student> studentList = new ArrayList<Student>();
studentList.add(new Student("정공자", 1));
studentList.add(new Student("비정공", 2));
studentList.add(new Student("컴공자", 3));
// List<?> : List에 어떤 객체를 담고 있어도 전달 가능
printPersonList(personList);
printPersonList(employeeList);
printPersonList(studentList);
// List<? super Employee> : List에 Employee, 상위클래스(person)만 담을 수 있음
printEmployeeList(employeeList);
printEmployeeList(personList); //상위 클래스
// printEmployeeList(studentList);
// List<? super Student> : List에 Student, 상위클래스(person)만 담을 수 있음
printStudentList(studentList);
printStudentList(personList); //상위 클래스
}
}