[java] 이것이 자바다 ch13 제네릭(Generic)
1. 제네릭
▷ 결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능
▷ <T>는 T가 타입 파라미터임을 뜻하는 기호. 타입이 필요한 자리에 T를 사용할 수 있음을 알려줍니다.
1. Box에 넣을 내용물로 content 필드를 선언
public class Box{ public ? content; }
2. 클래스 타입 선언 대신 Object 타입 선언public class Box{ public Object content; }
3. Object는 최상위 부모 클래스이므로 자동 타입 변환되어서 어떤 객체든 대입 가능합니다.Box box = new Box(); box.content = 모든 객체;
4. 강제 타입 변환String content = (String) box.content;
◎ 제네릭 예제
1. Box 클래스(제네릭 타입)
public class Box<T> { public T content; }
2. GenericExample 메인 클래스public class GenericExample { public static void main(String[] args) { //Box<String> box1 = new Box<String>(); Box<String> box1 = new Box<>(); box1.content = "안녕하세요."; String str = box1.content; System.out.println(str); //Box<Integer> box2 = new Box<Integer>(); Box<Integer> box2 = new Box<>(); box2.content = 100; int value = box2.content; System.out.println(value); } } // 출력 : // 안녕하세요. // 100
2. 제네릭 타입
▷ 결정되지 않은 타입을 파라미터로 가지는 클래스와 인터페이스
▷ 선언부에 '<>' 부호가 붙고 그 사이에 타입 파라미터들이 위치
public class 클래스명<A, B, ...>{ ... } public interface 인터페이스<A, B, ...>{ ... }
▷ 타입 파라미터는 일반적으로 대문자 알파벳 한 글자로 표현
▷ 외부에서 제네릭 타입을 사용하려면 타입 파라미터에 구체적인 타입을 지정.
지정하지 않으면 Object 타입이 암묵적으로 사용됩니다.
◎ 제네릭 타입 클래스 예제
1. Product 클래스(제네릭 타입)
// 제네릭 타입 public class Product<K, M> { // 필드 private K kind; private M model; // 메소드 public K getKind() { return this.kind; } public M getModel() { return this.model; } public void setKind(K kind) { this.kind = kind; } public void setModel(M model) { this.model = model; } }
2. Tv 클래스
public class Tv { }
3. Car 클래스
public class Car { }
4. GenericExample 메인 클래스
public class GenericExample { public static void main(String[] args) { // K는 Tv로 대체, M은 String으로 대체 Product<Tv, String> product1 = new Product<>(); // Setter 매개값은 반드시 Tv와 String을 제공 product1.setKind(new Tv()); product1.setModel("스마트Tv"); // Getter 리턴값은 Tv와 String이 됨 Tv tv = product1.getKind(); String tvModel = product1.getModel(); //--------------------------------------- //K는 Car로 대체, M은 String으로 대체 Product<Car, String> product2 = new Product<>(); // Setter 매개값은 반드시 Tv와 String을 제공 product2.setKind(new Car()); product2.setModel("SUV 자동차"); // Getter 리턴값은 Tv와 String이 됨 Car car = product2.getKind(); String carModel = product2.getModel(); } }
결과 값은 따로 출력이 되지 않습니다.
◎ 제네릭 타입 인터페이스 예제
1. Rentable 인터페이스(제네릭 타입)
public interface Rentable<P> { P rent(); }
2. Home 클래스
public class Home { public void turnOnLight() { System.out.println("전등을 켭니다."); } }
3. Car 클래스
public class Car { public void run() { System.out.println("자동차가 달립니다."); } }
4. HomeAgency 클래스
public class HomeAgency implements Rentable<Home> { @Override public Home rent() { return new Home(); } }
5. CarAgency 클래스
public class CarAgency implements Rentable<Car> { @Override public Car rent() { return new Car(); } }
6. GenericExample 클래스
public class GenericExample { public static void main(String[] args) { HomeAgency homeAgency = new HomeAgency(); Home home = homeAgency.rent(); home.turnOnLight(); CarAgency carAgency = new CarAgency(); Car car = carAgency.rent(); car.run(); } } // 출력 : // 전등을 켭니다. // 자동차가 달립니다.
◎ 제네릭 타입 박스 내용물 비교 예제
1. Box 클래스
public class Box<T> { public T content; // Box의 내용물이 같은지 비교 public boolean compare(Box<T> other) { boolean result = content.equals(other.content); return result; } }
2. GenericExample 메인 클래스public class GenericExample { public static void main(String[] args) { Box box1 = new Box(); box1.content = "100"; Box box2 = new Box(); box2.content = "100"; Box box3 = new Box(); box3.content = 100; boolean result1 = box1.compare(box2); System.out.println("result1: " + result1); boolean result2 = box1.compare(box3); System.out.println("result2: " + result2); } } // 출력 : // result1: true // result2: false
3. 제네릭 메소드
▷ 타입 파라미터를 가지고 있는 메소드, 타입 파라미터가 메소드 선언부에 정의
▷ 리턴 타입 앞에 <> 기호 추가하고 타입 파라미터 정의 후 리턴 타입과 매개변수 타입에서 사용
public <A, B, ...> 리턴타입 메소드명(매개변수, ...) { ... }
▷ 타입 파라미터 T는 매개값의 타입에 따라 컴파일 과정에서 구체적인 타입으로 대체
public <T> Box<T> boxing(T t) { ... }
1. Box<Integer> box1 = boxing(100);
2. Box<String> box2 = boxing("안녕하세요");
◎ 제너릭 메소드 예제
1. Box 클래스(제너릭 타입)
public class Box<T> { // 필드 private T t; // Getter 메소드 public T get() { return t; } // Setter 메소드 public void set(T t) { this.t = t; } }
2. GenericExample 메인 클래스
public class GenericExample { // 제네릭 메소드 public static <T> Box<T> boxing(T t){ Box<T> box = new Box<T>(); box.set(t); return box; } public static void main(String[] args) { // 제네릭 메소드 호출 Box<Integer> box1 = boxing(100); int intValue = box1.get(); System.out.println(intValue); // 제네릭 메소드 호출 Box<String> box2 = boxing("홍길동"); String strValue = box2.get(); System.out.println(strValue); } } // 출력 : // 100 // 홍길동
제너릭를 사용하는 이유는 형태가 지정되어 있지 않을 때 유동성을 주기 위해 주로 사용하는 방법입니다.
따라서 사용자가 원하는 때에 따라 int, String 등으로 자유롭게 형태를 바꿀 수 있습니다.
4. 제한된 타입 파라미터
▷ 모든 타입으로 대체할 수 없고, 특정 타입과 자식 또는 구현 관계에 있는 타입만 대체할 수 있는 타입 파라미터
public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) { ... }
▷ 상위 타입은 클래스뿐만 아니라 인터페이스도 가능합니다.
public <T extends Number> boolean compare(T t1, T t2){ double v1 = t1.doubleValue(); // Number의 doubleValue() 메소드 사용 double v2 = t2.doubleValue(); // Number의 doubleValue() 메소드 사용 return (v1 == v2); }
◎ 제한된 타입 파라미터 예제
public class GenericExample { // 제한된 타입 파라미터를 갖는 제네릭 메소드 public static <T extends Number> boolean compare(T t1, T t2) { // T의 타입을 출력 System.out.println("compare(" + t1.getClass().getSimpleName() + ", " + t2.getClass().getSimpleName() + ")"); // Number의 메소드 사용 double v1 = t1.doubleValue(); double v2 = t2.doubleValue(); return (v1 == v2); } public static void main(String[] args) { // 제네릭 메소드 호출 boolean result1 = compare(10, 20); System.out.println(result1); System.out.println(); // 제네릭 메소드 호출 boolean result2 = compare(4.5, 4.5); System.out.println(result2); } } // 출력 : // compare(Integer, Integer) // false // // compare(Double, Double) // true
5. 와일드카드 타입 파라미터
▷ 제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 범위에 있는 모든 타입으로 대체할 수 있는 타입 파라미터. ?로 표시
1. 자식 클래스만 사용 가능하도록 선언
→ 리턴타입 메소드면(제너릭타입<? extends Student> 변수) { ... }
2. 부모 클래스만 사용 가능하도록 선언
→ 리턴타입 메소드면(제너릭타입<? super Worker> 변수) { ... }
3. 모든 타입 다 사용 가능하도록 선언
→ 리턴타입 메소드면(제너릭타입<?> 변수) { ... }
◎ 와일드카드 타입 파라미터 사용 예제
1. Person 부모 클래스
public class Person { } class Worker extends Person{ } class Student extends Person{ } class HighStudent extends Student{ } class MiddleStudent extends Student{ }
2. Applicant 클래스public class Applicant<T> { public T kind; public Applicant(T kind) { this.kind = kind; } }
3. Course 클래스public class Course { // 모든 사람이면 등록 가능 public static void registerCourse1(Applicant<?> applicant) { System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) Course1을 등록함"); } // 학생만 등록 public static void registerCourse2(Applicant<? extends Student> applicant) { System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) Course2를 등록함"); } // 직장인 및 일반인만 등록 public static void registerCourse3(Applicant<? super Worker> applicant) { System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) Course3을 등록함"); } }
4. GenericExample 메인 클래스public class GenericExample { public static void main(String[] args) { // 모든 사람이 신청 가능 Course.registerCourse1(new Applicant<Person>(new Person())); Course.registerCourse1(new Applicant<Worker>(new Worker())); Course.registerCourse1(new Applicant<Student>(new Student())); Course.registerCourse1(new Applicant<HighStudent>(new HighStudent())); Course.registerCourse1(new Applicant<MiddleStudent>(new MiddleStudent())); System.out.println(); // 학생만 신청 가능 // Course.registerCourse2(new Applicant<Person>(new Person())); // Course.registerCourse2(new Applicant<Worker>(new Worker())); Course.registerCourse2(new Applicant<Student>(new Student())); Course.registerCourse2(new Applicant<HighStudent>(new HighStudent())); Course.registerCourse2(new Applicant<MiddleStudent>(new MiddleStudent())); System.out.println(); // 직장인 및 일반인만 신청 가능 Course.registerCourse3(new Applicant<Person>(new Person())); Course.registerCourse3(new Applicant<Worker>(new Worker())); // Course.registerCourse3(new Applicant<Student>(new Student())); // Course.registerCourse3(new Applicant<HighStudent>(new HighStudent())); // Course.registerCourse3(new Applicant<MiddleStudent>(new MiddleStudent())); } } // 출력 : // Person이(가) Course1을 등록함 // Worker이(가) Course1을 등록함 // Student이(가) Course1을 등록함 // HighStudent이(가) Course1을 등록함 // MiddleStudent이(가) Course1을 등록함 // // Student이(가) Course2를 등록함 // HighStudent이(가) Course2를 등록함 // MiddleStudent이(가) Course2를 등록함 // // Person이(가) Course3을 등록함 // Worker이(가) Course3을 등록함
제네릭 예제들을 다루다보니 타입을 지정하지 않고 같은 코드를 사용하고 싶을 때 사용하네요!
나중에 JSP쪽에서 더 많이 다룰 것 같으니 그때 좀 더 응용해볼게요~
많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~
