-
[Java]STEP2 - 2) 제네릭 메소드(Generic Method), 와일드카드, 제네릭 타입의 상속과 구현개발 공부/Java 2020. 11. 24. 19:43
본 게시글은 도서 "이것이 자바다" 동영상 강의를 듣고 기록하는 TIL(Today I Learned) 입니다.
모든 저작권은 출판사 한빛미디어와 신용권님께 있음을 알립니다.👍이번 게시물은 저번 게시물에 이어서 제네릭에 대해서 더 알아보겠습니다.
제네릭 메소드란 무엇이고, 와일드 카드의 개념, 제네릭 타입의 상속과 구현에 대해 알아보겠습니다.
◈ 제네릭 메소드 (Generic Method)
제네릭 메소드(Generic Method)란 매개 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드를 말한다.
다음과 같이 선언 할 수 있다.
// 제네릭 메소드 선언 // 매개 변수 타입: T // 리턴 타입: Box<T> public <T> Box<T> boxing(T t) { Box<T> box = new Box<T>(); box.set(t); return box; }
먼저 리턴 타입(int, boolean, String, Box<T> 등등)앞에 <T>와 같이 타입 파라미터를 기술한다.
그 다음 매개 변수에 제네릭 타입 파라미터를 기입하면 된다.
호출은 다음과 같이 할 수 있다.
public class BoxingMethodExample { public static void main(String[] args) { Box<Integer> box1 = <Integer>boxing(100); int intValue = box1.get(); Box<String> box2 = boxing("홍길동"); String strValue = box2.get(); } }
메소드를 호출 할 때, <Integer>boxing(100); 처럼 타입 파라미터를 구체적으로 명시하여도 되고,
단순히 boxing(100);으로 작성하여도 타입 파라미터를 Integer로 추정하여 대입하게 된다.
둘 중 어느 방법을 사용하더라도 상관 없다.
이러한 제네릭 메소드에 들어가는 타입 파라미터를 구체적으로 제한할 경우도 있을 수 있다.
예를 들면 <T>에 들어가는 타입이 여러 가지가 들어갔으면 좋겠지만, Byte Short Integer Long Double과 같은 숫자 타입만 넣고 싶은 경우이다. 그럴 경우에는 extends 를 통해 타입 파라미터를 제한할 수 있다.
// extends를 이용해 타입 제한 public static <T extends Number> int compare(T t1, T t2) { double v1 = t1.doubleValue(); double v2 = t2.doubleValue(); return Double.compare(v1, v2); }
위 제네릭 메소드에서 int 앞에 <T extends Number>라는 것이 보일 것이다.
즉 Number 클래스 또는 Number 클래스의 하위 타입 클래스만 사용할 수 있음을 의미한다.
그러므로 이 compare 제네릭 메소드에 String이나 기타 다른 타입이 삽입 되는 것을 제한 할 수 있게 된다.
단! 주의할 점이 있는데 두 가지가 있다!!!
1) 상위 타입은 클래스뿐만 아니라 인터페이스도 가능하다. 하지만 그렇다고 extends를 implements라고 쓰진 않는다.
▶ 예를 들면 AAA라는 인터페이스의 하위 구현 객체들만 compare 메소드에서 사용하고 싶다고 한다면
보통 <T implements AAA>라고 생각하기 쉬우나, <T extends AAA> 라고 써야한다는 것이다.
클래스이든 인터페이스이든 상관 없이 제네릭 메소드의 타입 제한에서는 extends만을 사용한다.
2) 제네릭 메소드의 중괄호{} 안에서 타입 파라미터 변수로 사용 가능한 것은 상위 타입의 멤버(필드, 메소드)로 제한한다.
▶ 즉 <T extends Number> 라고 제네릭 메소드의 타입을 제한했다면, 메소드의 실행 부분에서는 Number 타입의
필드와 메소드만 사용할 수 있다는 뜻이다. Integer에는 있고 Number에는 없는 필드나 메소드는 사용할 수 없다.
코드에서 ?를 일반적으로 와일드카드(wildcard)라고 부른다.
제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 구체적인 타입 대신에 와일드카드를 이용하여
다음과 같이 세 가지 형태로 사용할 수 있다.
1) <?> : 모든 클래스나 인터페이스 타입이 올 수 있다.
public static void registerCourse(Course<?> course) { System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents())); }
<?>가 사용되었으므로 Course에 모든 클래스나 인터페이스 타입이 올 수 있다.
2) <? extends 상위타입> : 상위 타입이나 하위 타입만 올 수 있다. (상위 클래스 제한)
public static void registerCourseStudent(Course<? extends Student> course) { System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents()) ); }
<? extends Student>가 사용되었으므로 Course에는 Student 타입이나 혹은 Student의 하위 타입만 가능하다.
즉, Student의 상위 타입은 불가능하다는 것이기 때문에 "상위 클래스 제한"하는 것이다.
3) <? super 하위타입> : 하위 타입이나 상위 타입이 올 수 있다. (하위 클래스 제한)
public static void registerCourseWorker(Course<? super Worker> course) { System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents())); }
<? super Worker>가 사용되었으므로 Course에는 Worker 타입이나 혹은 Worker의 상위 타입만 가능하다.
즉, Worker의 하위 타입이나 Worker와 동등한 타입은 올 수 없다는 것이므로, "하위 클래스 제한"하는 것이다.
◈ 제네릭 타입의 상속과 구현
제네릭 타입도 다른 타입과 마찬가지로 부모 클래스가 될 수 있다. 예를 들면 다음과 같이 선언할 수 있다.
// 제네릭 타입의 상속 public class ChildProduct<T, M, C> extends Product<T, M> {...}
ChildeProduct는 Product를 상속한 자식 클래스이다. 자식 클래스는 추가적으로 타입 파라미터를 가질 수 있다.
위 코드에서는 ChildProduct가 기존 T, M 타입 이외에 C를 가지고 있는 것을 볼 수 있다.
조금 더 상세히 선언을 해보면 아래와 같다.
// Product 부모 클래스 public class Product<T, M> { private T kind; private M model; public T getKind() { return this.kind; } public M getModel() { return this.model; } public void setKind(T kind) { this.kind = kind; } public void setModel(M model) { this.model = model; } }
// ChildProduct 자식 클래스 public class ChildProduct<T, M, C> extends Product<T, M> { private C company; public C getCompany() { return this.company; } public void setCompany(C company) { this.company = company; } }
ChildProduct 클래스에서는 부모 클래스인 Product 클래스의 kind, model 뿐만 아니라 company 까지 사용할 수 있게 된다.
제네릭 인터페이스를 구현한 클래스도 제네릭 타입이 된다.
// 제네릭 인터페이스 public interface Storage<T> { public void add(T item, int index); public T get(int index); }
현재 Storage<T> 라고 하는 제네릭 인터페이스에는 add와 get 메소드가 선언되어있다.
// 제네릭 구현 클래스 public class StorageImpl<T> implements Storage<T> { private T[] array; public StorageImpl(int capacity) { this.array = (T[]) (new Object[capacity]); } @Override public void add(T item, int index) { array[index] = item; } @Override public T get(int index) { return array[index]; } }
StorageImpl<T>는 Storage<T>를 구현한 제네릭 구현 클래스이다. add와 get 메소드를 오버라이딩 하였다.
이번 게시물은 여기서 마치겠습니다.
긴 글 읽어주셔서 감사합니다~~!!
'개발 공부 > Java' 카테고리의 다른 글
[Java]STEP2 - 1) 제네릭(Generic)이란?, 제네릭의 등장 배경과 필요성 (0) 2020.11.24 [Java]STEP1 - 22) Date 클래스, Calendar 클래스, Format 클래스 (0) 2020.10.19 [Java]STEP1 - 21) Wrapper(포장) 클래스, Math 클래스, Random 클래스 (0) 2020.10.18 [Java]STEP1 - 20) Arrays 클래스 (0) 2020.10.17 [Java]STEP1 - 19) 정규 표현식과 Pattern 클래스 (0) 2020.10.17