제네릭<>
제네릭 타입(class<T>, interface<T>)
public class 클래스명<T> {...} public interface 인터페이스명<T> {...}
제네릭의 장점 - 컴파일시 강한 타입 체크로 실행 시 에러를 사전에 방지. 타입변환(casting)을 제거
public class Box { private Object object; public void set(Object object) { this.object = object; } public Object get() { return object; } }
public class BoxExample { public static void main(String[] args) { Box box = new Box(); box.set("홍길동"); String name = (String) box.get(); System.out.println(name); box.set(555); int number = (int) box.get(); System.out.println(number); } }
타입 변환을 사용해야함
public class Box<T> { private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
public class BoxExample { public static void main(String[] args) { // TODO Auto-generated method stub Box<String> box1 = new Box<String>(); box1.set("홍길동"); String name = box1.get(); System.out.println(name); Box<Integer> box2 = new Box<Integer>(); box2.set(555); int number = box2.get(); System.out.println(number); } }
타입 변환 없이 사용
멀티 타입 파라미터(class<K, V, ....>, interface<K, V, ....>)
public class Product<T, M> { private T kind; private M model; public T getKind() { return kind; } public void setKind(T kind) { this.kind = kind; } public M getModel() { return model; } public void setModel(M model) { this.model = model; } }
public class ProductExample { public static void main(String[] args) { Product<Tv, String> product1 = new Product<Tv, String>(); //자바 7부터 Product<Tv, String> product2 = new Product<>(); product1.setKind(new Tv()); product1.setModel("스마트Tv"); Tv tv = product1.getKind(); String tvModel = product1.getModel(); } }
자바 7부터는 축약 사용 가능
제네릭 메소드(<T,R> R method(T t)) - 매개 변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드, 리턴 타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로 타입 파라미터를 사용
public <타입파라미터, ...> 리턴타입 메소드명(매개변수, ...){ ...} public <T> Box<T> boxing(T t) {...}
제네릭 메소드 호출 방법
리턴타입 변수 = <구체적타입> 메소드명(매개값); //명시적으로 구체적 타입을 지정 리턴타입 변수 = 메소드명(매개값); //매개값을 보고 구체적 타입을 추정 Box<Integer> box = <Integer>boxing(100);//타입 파라미터를 명시적으로 Integer로 지정 Box<Integer> box = boxing(100); //타입 파라미터를 Integer로 추정
public class Util2 { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { boolean keyCompare = p1.getKey().equals(p2.getKey()); boolean valueCompare = p1.getValue().equals(p2.getValue()); return keyCompare && valueCompare; } }
public class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public void setKey(K key) { this.key = key; } public void setValue(V value) { this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }
public class CompareMethodExample { public static void main(String[] args) { Pair<Integer, String> p1 = new Pair<Integer, String>(1, "사과"); Pair<Integer, String> p2 = new Pair<Integer, String>(1, "사과"); //구체적인 타입을 명시적으로 지 boolean result1 = Util2.<Integer, String>compare(p1, p2); if(result1) { System.out.println("논리적으로 동등한 객체입니다."); }else { System.out.println("논리적으로 동등하지 않는 객체입니다."); } Pair<String, String> p3 = new Pair<>("user1", "홍길동"); Pair<String, String> p4 = new Pair<>("user2", "홍길동"); //구체적인 타입을 추정 boolean result2 = Util2.compare(p3, p4); if(result2) { System.out.println("논리적으로 동등한 객체입니다."); }else { System.out.println("논리적으로 동등하지 않는 객체입니다."); } } }
제한된 타입 파라미터(<T extends 최상위타입>) - 구체적인 타입을 제한할 때 ex) 숫자 연산 메소드는 매개값으로 Number 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스만 가져야 할때 extends 키워드를 붙이고 상위 타입을 명시하고 인터페이스도 가능한데 implements를 안쓰고 extends를 사용
public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) { ...}
메소드의 중괄호 {} 안에서 타입 파라미터 변수로 사용한 것은 상위 타입의 멤버(필드, 메소드)로 제한
public class Util3 { public static <T extends Number> int compare(T t1, T t2) { double v1 = t1.doubleValue(); //Number의 doubleValue() 메소드 사 double v2 = t2.doubleValue(); return Double.compare(v1, v2); } }
public class BoundedTypeParameterExample { public static void main(String[] args) { //String str = Util3.compare("a", "b");Number 타입이 아님 //int -> Integer int result1 = Util3.compare(10, 20); System.out.println("result1 : " + result1); //double -> Double int result2 = Util3.compare(4.5, 3); System.out.println("result2 : " + result2); } }
와일드카드 타입(<?>, <? extends ...>, <? super ...>)
- 제네릭타입<?> : 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있음(제한 없음)
- 제네릭타입<? extends 상위타입> : 타입 파라미터를 대치하는 구체적인 타입으로 상위타입이나 하위 타입만 올 수 있음(상위 클래스 제한)
- 제네릭타입<? super 하위타입> : 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입이 옴(하위 클래스 제한)
public class Course<T> { private String name; private T[] students; public Course(String name, int capacity) { this.name = name; students = (T[]) (new Object[capacity]); System.out.println(Arrays.toString(students)+"끄억"+capacity); } public String getName() { return name; } public T[] getStudents() { return students; } //배열에 비어있는 부분을 찾아서 수강생을 추가 public void add(T t) { for(int i=0; i<students.length; i++) { students[i] = t; break; } } }
public class WildCardExample { 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("고등학생")); //모든 과정 등록 가 registerCourse(personCourse); registerCourse(workerCourse); registerCourse(studentCourse); registerCourse(highStudentCourse); //registerCourseStudent(personCourse); 학생이 아니므로 //registerCourseStudent(workerCourse); registerCourseStudent(studentCourse); registerCourseStudent(highStudentCourse); registerCourseWorker(personCourse); registerCourseWorker(workerCourse); //registerCourseWorker(studentCourse; 학생 이므로 //registerCourseWorker(highStudentCourse); } //모든 과정 public static void registerCourse(Course<?> course) { System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents())); } //학생 과정 public static void registerCourseStudent(Course<? extends Student> course) { System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents())); } //직장인과 일반인 과정 public static void registerCourseWorker(Course<? super Worker> course) { System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents())); } }
제네릭 타입의 상속과 인터페이스 구현도 가능하다
댓글
댓글 쓰기