제네릭스
자바 코드를 보다보면 <>안에 들어가 있는 형태를 볼 때가 있습니다.
이게 뭐지..? 라고 할수도 있지만 효율성을 높여준다는 말을 듣고 진지하게 임하게 되었습니다!!
제네릭스의 정의는 매개변수화된 자료형을 의미합니다. 클래스, 인터페이스, 메서드에 사용될 수 있습니다.
쉽게 풀어보면, 자료형에 타입을 지정할 수 있다는 것입니다.
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList();
number.add(10);
number.add(20);
number.add("30"); // comfile error 정수형만 들어가야되는데 문자형이기 때문
}
또한, 중복되는 메소드를 하나만 만들어도 공통으로 사용할 수 있습니다.
public class Use<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
public class Test {
public static void main(String[] args) {
Use<Integer> one = new Use<Integer>();
one.set(1); // one = 1
}
}
만약 밑에 "one"을 넣게되면 comfile error가 발생했을 것입니다.
차근 차근 예제를 통해 알아보겠습니다.
제너릭스 클래스으 기본 형태는 클래스명<매개변수 타입 리스트> 형태를 가집니다.
package generic;
class Data<T> {
T obj; // 인스턴스 변수 obj의 자료형이 T 상태입니다.
Data(){} // default 생성자
Data(T obj) { // 생성자 Data는 인수 1개이고, 자료형은 T입니다.
this.obj = obj;
}
T getObj() {
return obj;
}
void showType() {
System.out.println(obj.getCalss().getSimpleName());
}
}
public class Generic {
public static void main(String[] args) {
Data<Integer> d1 = new Data<Integer>(100);//Integer은 int의 wrapper Class 입니다 정수 100
System.out.println(d1.getObj()); //d1확인용 100
d1.showType(); //d1 Type확인용 java.lang.Integer
Data<String> d2 = new Data<String>("java");
System.out.println(d2.getObj()); // java
d2.showType(); // java.lang.String
}
}
여기서 Data를 제너릭 클래스라고 합니다. 제너릭으로 사용할 때 자료형으로 int,double을 사용할 수 없으며, 사용하려면 wrapper class인 Integer, Double등을 통해 사용할 수 있습니다.
주의사항!
제너릭 클래스에서 허용되지 않는 것
1. static 맴버에 대해서 타입 매개변수를 사용하면 안됩니다.
➡ static은 시스템이 실행되었을 때, 메모리로 바로 올려서 모든 객체에 대해 동일하게 동작해야하지만 T는 인스턴스 변수로 인해 달라질수 있기 때문입니다.
2. 제너릭 타입의 배열을 생성하면 안됩니다.
➡ 배열의 new 연산자 때문에 컴파일 시점에 타입 T가 무엇인지 알아야하는데 제너릭 타입을 사용하면 알수 없기 때문입니다.
cf) 여기서 wrapper class가 나오는데
주로 파싱할때나, 제너릭스에서 사용됩니다. unboxing하지 않으면 연산처리가 될수 없지만 null값을 처리할 수 있습니다.
cf) 타입 파라미터 네이밍 컨벤션
이름 T, V 뭘 넣어야 할까? 고민될수 있죠!!
컨벤션이 있다는 사실!
- E : element
- K : key
- V : value
- N : number
- T : type
- S, U, V : second, third, 4th types
이젠 조금 익숙해지는 것 같아요
이번엔 제한된 제너릭스에 대해 알아보아요!!
제너릭 클래스를 일정 용도로 사용하자고 했는데, 다른 용도로 사용하는 것이 꼭 발생하죠!
그래서 이를 제한하기 위해서 사용됩니다.
class A<T extends Alpha> {...}
위에 Alpha로 extends를 걸어주면 T는 Alpha 또는 Alpha의 자식 클래스에서만 사용할 수 있습니다.
cf) class A<T extends Alpha & Beta>
&로 제한을 넓힐 수도 있습니다.
예를 통해 알아보겠습니다.
class Data<T extends Number> {
T obj;
Data(T ob) {
obj = ob;
}
int calcMultiple(int n) {
return obj.intValue()*n;
}
}
public class Test {
public static void main(String[] args) {
Data<Integer> d = new Data<Integer>(100);
int result = d.calcMultiple(5);
System.out.println(result); // 500
Data<Double> e = new Data<Double>(1.1);
int result2 = e.calcMultiple(5);
System.out.println(result2); //5
}
}
Number Class에 extends를 한다고 하면, Number 내에 있는 클래스들만 사용할 수 있습니다.
아래의 cf)를 참고해주세요.
cf) Number Class
Abstract 클래스로 종류는 Short, Integer, Long, Float, Double, Byte가 있습니다.
메소드로는
intValue, doubleValue 등이 있습니다.
cf) Class 계층
class는 가장위에 Object계층이 있습니다!
밑에 메소드는
clone, equals, toString, getClass, hashCode, finalize, notify, notifyAll, wait 등이 있습니다.
고생하셨어요~!
마지막으로 와일드카드 인수에 대해 알아보겠습니다
미리 맛보기
<? extends T> // 와일드 카드의 상한 제한. T와 그 자손들만 사용할 수 있다.
<? super T> //와일드 카드의 하한 제한. T와 그 조상들만 사용할 수 있다.
<?> //제한 없음. <? extends Object>
제네릭스 제한을 와일드 변수를 사용하면 조금 더 변별력 있게 사용할 수 있습니다.
예를 들어 설명하겠습니다.
registerCourse()는 모든 수강생이 들을 수 있는 과정을 등록하고,
registerCourseStudent()는 학생만 들을 수 있는 과정을 등록하고,
registerCourseWorker()는 직장인만 들을 수 있는 과정을 등록합니다.
package test;
import java.util.Arrays;
public class WildCardExample {
public static void registerCourse(Course<?> course) {
System.out.println(course.getName() + " 수강생: " + Arrays.deepToString(course.getStudents()));
}
// Student 클래스의 상위 타입을 제한
public static void registerCourseStudent(Course<? extends Student> course) {
System.out.println(course.getName() + " 수강생: " + Arrays.deepToString(course.getStudents()));
}
// Worker 클래스의 하위 타입을 제한
public static void registerCourseWorker(Course<? super Worker> course) {
System.out.println(course.getName() + " 수강생: " + Arrays.deepToString(course.getStudents()));
}
public static void main(String[] args) {
Course<Person> personCourse = new Course<Person>("일반인과정", 5);
personCourse.add(new Person("일반인"));
personCourse.add(new Worker("직장인"));
personCourse.add(new Student("학생"));
personCourse.add(new HighStudent("고등학생"));
Course<Worker> workerCourse = new Course<Worker>("직장인과정", 5);
workerCourse.add(new Worker("직장인"));
Course<Student> studentCourse = new Course<Student>("학생과정", 5);
studentCourse.add(new Student("학생"));
studentCourse.add(new HighStudent("고등학생"));
Course<HighStudent> highStudentCourse = new Course<HighStudent>("고등학생과정", 5);
highStudentCourse.add(new HighStudent("고등학생"));
/**
* Course<?>
* - 모든 타입이 가능함.
*/
registerCourse(personCourse);
registerCourse(workerCourse);
registerCourse(studentCourse);
registerCourse(highStudentCourse);
System.out.println();
/**
* Course<? extends Student>
* - Student 클래스나 하위타입인 HighStudent 클래스만 가능
*/
//registerCourseStudent(personCourse); // X
//registerCourseStudent(workerCourse); // X
registerCourseStudent(studentCourse);
registerCourseStudent(highStudentCourse);
System.out.println();
/**
* Course<? extends Worker>
* - Worker 클래스나 상위타입인 Person 클래스만 가능
*/
registerCourseWorker(personCourse);
registerCourseWorker(workerCourse);
//registerCourseWorker(studentCourse); // X
//registerCourseWorker(highStudentCourse); // X
}
[출처] [Java/자바] - 제네릭(Generic) - 와일드카드(Wildcard) 타입<?>, <? extends ...>, <? super ...>|작성자 주현
위 코드의 각 메서드들을 나타내면 다음과 같습니다.
따라서 코드에서 registerCourser() 메소드는 모든 과정이 등록 가능하며,
registerCourseStudent() 메소드는 학생 과정(Student, HighStudent)만 등록이 가능하며,
registerCourseWorker() 메소드는 직장인과 일반인(Worker, Person)만 등록이 가능합니다.
참고 도서 : 이젠 나도! 자바
'기술 스텍 > Java' 카테고리의 다른 글
[Collection] ArrayList를 왜 List로 받아야 하는가? List의 종류 (0) | 2023.06.24 |
---|---|
서블릿(survlet) (0) | 2023.06.23 |
java 기초(6) (0) | 2023.01.23 |
java 기초(5) (0) | 2023.01.23 |
java 기초(4) (0) | 2023.01.23 |