제네릭 메서드
제네릭이란?
💡 여러가지 타입을 자원으로 사용하기 위해 다양성을 가진 제네릭 타입을 명시해 주어 해당 타입으로 클래스 및 메서드를 프로그래밍 하는 기법
- 클래스 전체를 제네릭으로 선언하는 대신, 일반 클래스 내부의 특정 메서드만 제네릭으로 선언할 수 있다.
- 이를
제네릭 메서드라 하며, 리턴 타입 또는 입력매개변수 타입을 제네릭 타입 변수로 선언한다. 제네릭 클래스가 객체를 생성하는 시점에 실제 타입을 지정하는 것과 달리 제네릭 메서드는 호출되는 시점에 실제 제네릭 타입을 지정한다.
접근 지정자 <T> T 메서드명(T t){}
접근 지정자 <T, V> T 메서드명(T t, V v){}
접근 지정자 <T> Void 메서드명(T t){}
접근 지정자 <T> T 메서드명(int a){}
- 제네릭 메서드에서 입력매개변수를 제네릭타입으로 사용할 때에는 호출과정에서 제네릭타입을 생략할 수 있다.
class GenericMethods{
public <T> T method1(T t){
return t;
}
public <T> boolean method2(T t1, T t2){
return t1.equals(t2);
}
public <K,V> void method3(K k, V v){
System.out.println(k+":"+v);
}
}
class GenericMethod {
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
String str1 = gm.<String>method1("안녕");
String str2 = gm.method1("안녕");
System.out.println(str1);
System.out.println(str2);
}
}
제네릭 메서드 내에서 사용할 수 있는 메서드
- 제네릭 메서드는 문법적으로 모든 타입이 올 수 있도록 허용하고 있기 때문에 Object 객체의 메서드만 활용할 수 있다.
제네릭 타입의 범위 제한
- 제네릭 타입을 올 수 있는 실제 타입의 종류를 제한하는 것
제네릭 클래스의 타입 제한
- 제네릭 클래스의 정의 과정에서 제네릭 타입을 제한하는 방법은
<제네릭 타입 변수 extends 상위 클래스> 와 같이 제네릭 타입으로 대입될 수 있는 최상위 클래스를 extends 키워드와 함께 정의 하는 방법
접근 지정자 class 클래스명 <T extends 최상위클래스/인터페이스명>{
}
- 최상위 클래스 및 인터페이스 또는 그 하위 클래스 타입만을 지정할 수 있다.
- 주의할점은 뒤에 나오는 요소가 클래스이든 인터페이스이든 항상
extends 키워드를 사용한다.
제네릭 메서드의 타입 제한
- 제네릭 클래스와 마찬가지로 <제네릭 타입 변수 extends 상위 클래스> 와 같이 올 수 있는 최상위 타입을 정의하며, 클래스와 인터페이스 모두
extends 키워드를 사용한다
접근 지정자 <T extends 최상위 클래스/인터페이스명> T 메서드명 (T t){}
- 타입을 제한하게 되면 최상위 클래스 하위 메서드를 활용할 수 있다.
제네릭 메서드에서의 제네릭 타입 제한 범위 설정
class A{
public <T extends Number> void method1(T t){
System.out.println(t.intValue());
}
}
interface MyInterface{
public abstract void print();
}
class B{
public <T extends MyInterface> void method1(T t){
t.print();
}
}
public class BoundedTypeOfGenericMethod {
public static void main(String[] args) {
A a = new A();
a.method1(5.8);
B b = new B();
b.method1(new MyInterface() {
@Override
public void print() {
System.out.println("print() 구현");
}
});
}
}
제네릭 클래스 객체의 제네릭 타입
리턴 타입 메서드명(제네릭 클래스명<제네릭 타입명> 참조 변수명){}
리턴 타입 메서드명(제네릭 클래스명<?> 참조 변수명){}
리턴 타입 메서드명(제네릭 클래스명<? extends 상위 클래스/인터페이스> 참조 변수명){}
리턴 타입 메서드명(제네릭 클래스명<? super 하위 클래스/인터페이스> 참조 변수명){}
- 첫번째는 객체의 제네릭 타입을 특정 타입으로 확정하는 방법
- 두번째는 제네릭 타입 변수에 <?> 사용할 때로 제네릭 타입으로 어떤 것이 대입되든 해당 제네릭 객체이기만 하면 매개변수로 사용할 수 있는 것을 의미
- 세번째는 <? extends 상위 클래스/인터페이스>와 같이 표기하는 방법으로, 상위 클래스의 자식 클래스타입이 제네릭 타입으로 대입된 객체가 매개변수로 올 수 있다.
- 네번째 <? super 하위클래스/인터페이스> 는 extends와 반대 개념으로 제네릭 타입을 하위 클래스 또는 하위 클래스의 부모 클래스 타입이 올 수 있다는 것을 의미한다.
제네릭 클래스의 상속
- 부모 클래스가 제네릭 클래스일 때, 이를 상속한 자식 클래스도 제네릭 클래스가 된다.
- 즉 제네릭 타입 변수를 자식 클래스가 그대로 물려 받는다.
- 또한 자식 클래스는 제네릭 타입 변수를 추가해 정의할 수 있다.
class Parent<K,V>{}
class Child<K,V> extends Parent{}
class Parent<K>{}
class Child<K,V> extends Parent<K>{}
제네릭 메서드의 상속
- 제네릭 메서드를 포함한 일반 클래스를 상속해 자식 클래스를 생성할 때에도 부모 클래스 내의 제네릭 메서드는 그대로 자식 클래스에 상속된다.
package Resource;
class Parent{
<T extends Number> void print(T t){
System.out.println(t);
}
}
class Child extends Parent{
}
public class InheritanceGenericMethod {
public static void main(String[] args) {
Parent p = new Parent();
p.print(3);
p.<Integer>print(10);
Child c = new Child();
c.print(10.1);
c.<Double>print(10.2);
}
}