클래스에서 사용할 타입을 클래스 외부에서 설정하는 것
클래스 내부에서 사용하는 데이터의 타입(Type)을 클래스의 인스턴스를 생성할 때 결정하는 것을 의미한다
여러가지 자료형을 허용하고 싶을 때 Object를 선언 할 수 있지만, 원하지 않는 자료형을 입력 되었을 시 오류를 컴파일 시점에 잡아낼 수 가 없다
제네릭은 타입을 입력 받을 때 사용할 수 있는 타입을 명시 할 수 있다.
제너릭 클래스는 클래스명 뒤에 <>을 붙여 안에 타입파라미터를 넣어준다
class GenericFoo<T> { // T: 타입 파라미터
}
타입 파라미터는 클래스 선언시 아직 결정되지 않은 타입이다
타입 파라미터는 참조형 타입만 올 수 있다
클래스안에서 타입파라미터를 자료형으로 인스턴스 변수와 메서드를 선언할 수 있다.
class GenericFoo<T> {
String name;
T memberVar;
public GenericFoo(String name, T memberVar) { // 어떤타입이 들어올지 모름
this.name = name;
this.memberVar = memberVar;
} // 생성자에는 클래스명 뒤에 <T>를 생략
인터페이스도 제너릭으로 생성할 수 있다
interface GenericInterface<T>{ //인터페이스도 제네릭이 가능
}
타입 파라미터의 갯수는 여러개가 될 수 있다
class HashMap<K,V>{ // 여러개의 타입 파라미터도 쓸 수 있다.
}
타입 파라미터의 자료형은 클래스의 객체 생성시 입력해 준다
public class Generics {
public static void main(String[] args) {
GenericFoo<String> foo = new GenericFoo<String>("name","var");//<dataType>
GenericFoo<String> foo1 = new GenericFoo<>("name","var");// 생성자에 DataType은 생략가능
System.out.println(foo.name);// name
System.out.println(foo.memberVar);// var
}
}
객체 생성시 자료형과 생성자 뒤에 형식으로 입력한다
자료형 뒤에 을 써준뒤 생성자에 DataType은 생략이 가능하다
문법적으로 문제가 있는경우
정적 변수의 자료형으로 타입파라미터를 사용할 수 없다
class GenericBar<T>{
static T classVar; // not possible
}
정적 메서드의 자료형으로 타입파라미터를 사용할 수 없다 ex) static void method(T var){}
static void method(T var){
return var;
} // not possible too or two
문법적으로는 문제가 없지만 안정성 문제로 제한이 되는 경우
안정성 문제 : 타입 파라미터가 어떤 것으로 구현되 있을지 모르는 상황에서 안정성에 문제가 생길 수 있는 상황
타입파라미터 형으로 객체를 생성할 수는 없다.
T memberVar = new T(); // not possible
instacneof의 비교 대상이 될 수 없다
ex) Object obj = new Object();
if(obj instanceof T) //not possible
제너릭 클래스와 인터페이스 모두 상속과 구현이 가능하다
class GFoo<T> {
}
interface IGFoo<T>{
}
class GIGFoo<K, T, D> extends GFoo<T> implements IGFoo<D>{
}
부모 클래스와 인터페이스 명에도 타입 파라미터를 표시해주어야 한다
상속받은 클래스는 제너릭 클래스가 되고 각각 상속이나 구현하는 타입 파라미터를 표시해준다
자식 클래스에서 타입 파라미터를 추가할 수도 있다.
타입 파라미터를 표시할 때는 각각의 파라미터를 독립적인 타입 파라미터로 설정 할수 있다
혹은 각각의 파라미터를 하나의 타입 파라미터로 통합 시킬 수 도 있다
class IGIFooTwo<T> extends GFoo<T> implements IGFoo<T>{
}
// extneds를 이용해서 부모 클래스와 인터페이스도 제한할 수 있다.
{//Number는 추상클래스를 상속하고 있는애들만 사용할 수 있음
//요 추상클래스도 상속하고 있으며 요인터페이스도 구현해야한다
class GenericTypeLimitation<T extends Number & Cloneable >{
GenericTypeLimitation<Number> genericTypeLimitation = new GenericTypeLimitation<>();
}
}
제너릭 클래스의 타입 파라미터 뒤에 extends를 하여 타입 파라미터에 올 수 있는 타입을 제한할 수 있다.
생략시 extedns Object를 하는 것과 동일하다 (Object 는 모든 클래스의 부모 클래스이기 때문)
class GenericNoTypeLimit<T extends Object>{} // == class GenericNoTypeLimit<T>{}
extends 뒤에 클래스명 작성시 해당 클래스를 상속하고 있는 클래스만 타입으로 입력할 수 있다(본인포함)
class GenericTypeLimitation<T extends Number >{
GenericTypeLimitation<Number> genericTypeLimitation = new GenericTypeLimitation<>();
}
타입을 제한할 클래스와 인터페이스를 동시에 사용할 수 있다
class GenericTypeLimitation<T extends Number & Cloneable >{
}
&를 통해서 앞에 클래스를 쓰고 뒤에 인터페이스를 사용한다
앞에 클래스를 상속하고 뒤에 인터페이스를 구현한 클래스로 제한한다는 뜻이다.
클래스와 인터페이스는 독립적인 요소로 아무 클래스나 해당 인터페이스를 구현하여 사용할 수 있다.
제너릭 메서드는 클래스에 타입파라미터를 사용한 것이 아닌 메서드에 타입파라미터를 선언한 것이다
public <T> T method(T x){
return x;
}
제너릭 메서드의 타입파라미터는 클래스 타입파라미터와 별개이다
public static <T> T staticMethod(T t){
return t;
}
제너릭 클래스와는 다르게 제너릭 메서드에서는 타입 파라미터를 자료형으로 선언할 수 있다
이는 제너릭 클래스와는 다르게 타입 파라미터를 메서드 자체에서 입력할 수 있기 때문이다
제네릭 메소드는 타입파라미터 자료형을 따로 정하지 않고 오버로딩 된 것 처럼 Arguments에 입력하면 자동으로 해당 자료형을 설정한다
public class Generics {
public static void main(String[] args) {
System.out.println(GenericMethod.staticMethod("abcd"));//abcd
System.out.println(GenericMethod.staticMethod("1234"));//1234
}
}
제너릭 메서드는 자료형이 정해져있지 않기 때문에 런타임때 정의 되어 지는 동적바인딩을 사용한다
자바파일을 바이트코드로 변환하는 컴파일일 과정에 자료형이 정의 되는 것을 정적바인딩이라고 한다
컴파일된 파일을 실제 동작하게 하는 런타임 때 메서드가 정의되는 것을 동적 바인디이라고 한다
와일드카드란, 제네릭 클래스의 객체를 메소드의 매개변수로 받을 때, 그 객체의 타입 변수를 제한하는 것을 말한다
class WildGeneric<T>{
}
class WildCard{
public void method1(WildGeneric<?> x){}
public void method1_eq(WildGeneric<? extends Object> x){}
와이드카드는 입력받을 클래스명뒤에 타입파라미터 자리에 ? 입력하여 표현할수 있다
super 키워드는 해당 class를 조상으로 두고 있는 클래스를 뜻함 (본인포함)
class WildFoo{
}
class WildBar extends WildFoo{
}
class WildGeneric<T>{
}
public void method2(WildGeneric<? extends WildFoo> x){}
와일드 카드 대신 특정 자료형을 사용하는 것도 가능하다
public void method4(WildGeneric<String> x){}
실행부
public class WildCardTest {
public static void main(String[] args) {
WildCard wildCard = new WildCard();
WildGeneric<String> wildGeneric = new WildGeneric<String>();
wildCard.method1(wildGeneric); //<?>이기에 아무 자료형이나 가능
wildCard.method4(wildGeneric); //String 이기에 가능
// wildCard.method2(wildGeneric); // String이기에 불가능
}
}