Java Generic - 메소드
제네릭에서 제공하는 제네릭 메소드가 있다.
제네릭 타입과 제네릭 메소드는 서로 다른 기능을 제고앟ㄴ다.
- 제네릭 타입
- 제네릭 메소드
제네릭 메소드 사용
GMethod.class
public class GMethod {
public static Object objMethod(Object obj) {
System.out.println("Object print = " + obj);
return obj;
}
public static <T> T callMethod(T t) {
System.out.println("Generic print = " + t);
return obj;
}
public static <T extends Number> T callNumberMethod(T t) {
System.out.println("Bound print = " + t);
return obj;
}
}
<T> T
반환을 가진callMethod(T obj)
메소드가 제네릭 메소드이다.- 제네릭 메소드에도
<T extends Number>
파라미터 제한(상한)을 사용할 수 있다.
제네릭 타입은 public class GMethod<T> {...}
클래스 선언 부분에 있는 것이 제네릭 타입이다.
이제 각자의 메소드 호출 방법을 살펴보자.
Object 반환 메소드 호출
Integer i = 10;
// 오브젝트를 받을 수 있다.
GMethod.objMethod(i);
// Object 반환은 변화에 문제가 생긴다. 다운 캐스팅이 필수이다.
Object obj = GMethod.objMethod(i);
Integer intObj = (Integer) GMethod.objMethod(i);
Object print = 10
- Object 인자로 전달할 수 있다.
- 반환에는 변화에 문제가 생긴다. 반환는 Object 이므로 다운 캐스팅이 필요하다.
- Integer 변수에 담으려면 (Integer) 키워드로 명시해주어야 한다.
<> 제네릭 메소드 호출
Integer i = 10;
// 타입 인자(Type Argument)를 명시적으로 전달한다.
System.out.println("명시적인 타입으로 인자를 전달하기");
Integer result1 = GMethod.<Integer>callMethod(i);
Integer result2 = GMethod.<Integer>callNumberMethod(i);
명시적인 타입으로 인자를 전달하기
Object print = 10
Bound print = 10
정리
- Generic Type
- 정의 방법: GenericClass<T>
- 타입 인자 전달: 객체 생성 시점
- new GenericClass<Integer>
- Generic Method
- 정의 방법: <T> T GMethod(T t)
- 타입 인자 전달: 메소드 호출 시점
- GMethod.<Integer>callMethod(i)
제네릭 메소드는 호출하는 시점에서 타입이 결정된다.
제네릭 메소드 정리
- 전체가 아닌 특정 클래스 단위로 제네릭 사용된다.
- 메소드 정의에는 반환 타입에서 다이아몬드
<T>
타입 매개변수를 적어준다. - 메소드를 실제 호출 시점에 사용해 <Integer> 타입을 지정해주면 지정된 타입으로 메소드가 호출된다.
제네릭 메소드는 호출하는 시점에서 타입 인자를 전달해 타입을 지정해주는 것이다. 타입이 지정되면 비로서 메소드가 호출된다.
제네릭 메소드 - static, 인스턴스
인스턴스 메소드와 static 메소드에 제네릭 메소드 적용 가능하다.
class Box<T> {
static <V> V staticMethod(V v) {} // 스태틱에 제네릭 메소드 적용
<Z> Z instanceMethod(Z z) {} // 인스턴스에 제네릭 메소드 적용
}
- static 메소드는
<V> V
제네릭 메소드로 사용 가능하다. - instant 메소드는
<Z> Z
제네릭 메소드로 사용 가능하다.
static 메소드 사용에 주의사항
객체 생성 시점인 제네릭 타입(class Box<T>)은 인스턴스 메소드에 사용이 가능하다. 그렇지만 이 제네릭으로 static 메소드에 사용은 할 수 없다. 그 이유는 static 메소드는 클래스 단위이다. 객체 시점에 사용하는 제네릭 타입은 사용할 수 없다.
static 메소드에 사용해야한다면 제네릭 메소드로 사용해주어야 한다.
제네릭 타입 - static, 인스턴스
class Box<T> {
T instanceM(T t) {} // 가능
// static T staticM(T t) {} // 제네릭 타입으로 T 타입 사용 불가
}
- 제네릭 타입
Box<T>
객체 생성 시점에 타입이 결정되도록 하였다. - instance 메소드에 제네릭 타입을 붙여 사용 가능하다.
- static 메소드에 제네릭 타입을 붙이면 사용이 불가능하다.
제네릭 - 파라미터 제한(상한)
제네릭 메소드는 파라미터 제한을 걸 수 있다.
class Box<T> {
public static <T extends Number> T numberM(T t) {
return t;
}
}
- 제네릭 메소드에서
extends Number
키워드를 붙여 숫자형 인자만 받도록 하였다.
Box.<Integer>numberM(100);
// Box.<String>numberM("100s"); // 오류 발생함
- 정의한
extends Nubmer
외의 타입은 오류를 발생 시켜 타입 안정성을 높였다.
제네릭 메소드 타입 추론
호출 시 <Integer> 타입 인자를 아래와 같이 매번 전달하는 것이 불편할 수 있다.
Integer i = 10;
Integer res = Box.<Integer>numberM(i);
- 자바 컴파일러는
numberM(...)
인자로 전달되는i
타입을Integer
알 수 있다. - 반환 타입이
Integer res
라는 힌트가 충분히 많으므로 추론이 가능하다.
Integer i = 10;
Integer res1 = Box.numberM(i);
Integer res2 = Box.numberM(100);
Double res3 = Box.numberM(100.0);
- 메소드 호출에 사용한 다이아몬드 타입을 제거한다.
- 자바 컴파일러가 타입을 추론하여 알아내고 타입이 맞는 메소드를 호출한다.
코드에서 메소드 호출에 사용한 타입이 제거되었고,
정의한 메소드에서도 제네릭 메소드만 있고 어떤 기본형, 참조형 타입인지 잘 모를 수 있지만, 자바 컴파일러가 알아서 추론하여 메소드로 인자를 전달하게 된다.