# Generic : 클래스 내부에서 사용할 데이터 타입을 나중에 인스턴스를 생성할 때 확정하는 것
변수의 데이터 타입과 관계가 있다.
class Person <T> //인스턴스 생성 시 String이라는 데이터 타입이 들어감
{
public T info; // T는 와일드 카드
}
Person<String> p1 = new Person<String>(); // 앞 = 뒤가 똑같은 형태를 취한다.
Person<StringBulider> p2 = new Person<StringBuilder>();
- 제네릭을 사용하는 이유 : 타입의 안정성과 코드 중복 제거의 편의성을 모두 잡기 위해 도입된 것
ㄴ 중복제거를 위해 데이터타입을 object로 주는 경우 목적성에 맞지 않는 데이터타입이 들어올 가능성이 있으며,
이로 인해 컴파일 에러는 발생하지 않으나 RuntimeException이 발생하므로 타입이 안전하지 않다.
이러한 문제점들을 해결하기 위해 제네릭이라는 기능을 따로 사용하는 것이다.
- 복수의 제네릭 사용 시
class Person <T , S>
{
}
① 메소드의 매개변수와 마찬가지로 ,로 구분해준다.
② 이름이 달라야 한다.
- 제네릭의 특성 : <>에는 기본자료형이 올 수 없고, 참조자료형만 올 수 있다.
만약 기본자료형을 사용해주어야 한다면
Wrapper Class로 기본자료형을 객체로 만들어 사용해준다.
- Generic 메소드
ex)
class AAA
{
public String toString()
{
return "Class AAA";
}
}
class BBB
{
public String toString()
{
return "Class BBB";
}
}
class InstanceTypeShower
{
int showCnt=0;
public <T> void showInstType(T inst)
{
System.out.println(inst); // 참조변수를 넣어주면 자동으로 ToString 호출됨
showCnt++;
}
void showPrintCnt()
{
System.out.println("Show count: "+showCnt);
}
}
class IntroGenericMethod
{
public static void main(String[] args)
{
AAA aaa=new AAA();
BBB bbb=new BBB();
InstanceTypeShower shower=new InstanceTypeShower();
shower.<AAA>showInstType(aaa); // 제네릭으로 AAA 넘겨줌
shower.<BBB>showInstType(bbb); // < > 생략 가능
shower.showPrintCnt();
}
}
ex)
class AAA
{
public String toString()
{
return "Class AAA";
}
}
class BBB
{
public String toString()
{
return "Class BBB";
}
}
class InstanceTypeShower2
{
public <T, U> void showInstType(T inst1, U inst2) // AAA, BBB로 바뀌게 됨
{
System.out.println(inst1); // 결과 : Class AAA
System.out.println(inst2); // 결과 : Class BBB
}
}
class IntroGenericMethod2
{
public static void main(String[] args)
{
AAA aaa=new AAA();
BBB bbb=new BBB();
InstanceTypeShower2 shower=new InstanceTypeShower2();
shower.<AAA, BBB>showInstType(aaa, bbb); // < > 생략가능 - 참조변수 자료형 타입으로 구별 가능하기 때문!
shower.showInstType(aaa, bbb);
}
}
ex) 에러 예시
interface SimpleInterface
{
public void showYourName();
}
class UpperClass
{
public void showYourAncestor()
{
System.out.println("UpperClass");
}
}
class AAA extends UpperClass implements SimpleInterface // UpperClass 상속받고 SimpleInterface 구현
{
public void showYourName()
{
System.out.println("Class AAA");
}
}
class BBB extends UpperClass implements SimpleInterface // UpperClass 상속받고 SimpleInterface 구현
{
public void showYourName() // 메소드 오버라이딩
{
System.out.println("Class BBB");
}
}
class BoundedTypeParam
{
public static <T> void showInstanceAncestor(T param)
{
((SimpleInterface)param).showYourName(); // 범용성이 깨지고 있음
}
public static <T> void showInstanceName(T param)
{
((UpperClass)param).showYourAncestor();
}
public static void main(String[] args)
{
AAA aaa=new AAA();
BBB bbb=new BBB();
showInstanceAncestor(aaa);
showInstanceName(aaa);
showInstanceAncestor(bbb);
showInstanceName(bbb);
}
}
ㄴ 제네릭은 모든 것을 일반화 시킬 수 있어야 하는데
모든 클래스에 showYourName이라는 메소드가 존재하지는 않기 때문에 에러가 발생한다.
에러를 잡기 위해선 위처럼 형변환을 해줘야 한다.
대신 범용성이 떨어지게 된다.
- 이를 해소하기 위해선 아래처럼 인터페이스로 구현한 것으로 extends라는 키워드를 사용하여 한정지을 수 있다.
(제네릭을 상속으로 제한하는 것)
//Tip : 제네릭에선 인터페이스를 제한할 때 implements를 사용하지 않고 extends를 사용해준다!
ex) 해소 예시
interface SimpleInterface
{
public void showYourName();
}
class UpperClass
{
public void showYourAncestor()
{
System.out.println("UpperClass");
}
}
class AAA extends UpperClass implements SimpleInterface
{
public void showYourName()
{
System.out.println("Class AAA");
}
}
class BBB extends UpperClass implements SimpleInterface
{
public void showYourName()
{
System.out.println("Class BBB");
}
}
class BoundedTypeParam2
{
public static <T extends SimpleInterface> void showInstanceAncestor(T param) // extends로 구현받는 것까지로 한정짓고 있다
{
param.showYourName();
}
public static <T extends UpperClass> void showInstanceName(T param) // extends로 상속받은 것까지로 한정짓고 있다
{
param.showYourAncestor();
}
public static void main(String[] args)
{
AAA aaa=new AAA();
BBB bbb=new BBB();
showInstanceAncestor(aaa); // 결과 : Class AAA
showInstanceName(aaa); // 결과 : UpperClass
showInstanceAncestor(bbb); // 결과 : Class BBB
showInstanceName(bbb); // 결과 : UpperClass
}
}
- 배열도 제네릭으로 넘길 수 있다.
ex)
class IntroGenericArray
{
public static <T> void showArrayData(T[] arr)
{
for(int i=0; i<arr.length; i++)
System.out.println(arr[i]);
}
public static void main(String[] args)
{
String[] stArr=new String[]{
"Hi!",
"I'm so happy",
"Java Generic Programming"
};
showArrayData(stArr);
}
}
ex)
class Fruit
{
public void showYou()
{
System.out.println("난 과일입니다.");
}
}
class Apple extends Fruit
{
public void showYou() // 메소드 오버라이딩
{
super.showYou();
System.out.println("난 붉은 과일입니다.");
}
}
class FruitBox<T>
{
T item;
public void store(T item) { this.item=item; } // 저장하는 메소드
public T pullOut() { return item; } // 꺼내는 메소드
}
class IntroWildCard
{
public static void openAndShowFruitBox(FruitBox<? extends Fruit> box) // ⓐ Fruit과 Fruit을 상속하는 모든 것이 저장할 수 있다
{
Fruit fruit=box.pullOut(); // 박스에서 꺼내는 것
fruit.showYou(); // box1 결과 : 난 과일입니다. box2 결과 : 난 붉은 과일입니다.
}
public static void main(String[] args)
{
FruitBox<Fruit> box1=new FruitBox<Fruit>();
box1.store(new Fruit());
FruitBox<Apple> box2=new FruitBox<Apple>(); //Apple 저장 지정하는 것
box2.store(new Apple());
openAndShowFruitBox(box1);
openAndShowFruitBox(box2); //Apple이 Fruit을 상속받고 있기 때문에 box2를 넣을 수 있는 것
}
}
ex) ⓐ 예시
public void hi(AAA aaa) // AAA와 AAA를 상속받는 모든 것이 매개변수로 들어갈 수 있다.
{
}
ㄴ 상위클래스가 하위클래스의 객체의 주소값을 참조할 수 있기 때문이다.
'SW > Java' 카테고리의 다른 글
[필기정리]Day21 - 문제 step2 (0) | 2020.07.08 |
---|---|
[필기정리]Day21 - 문제 step1 (0) | 2020.07.08 |
[필기정리]Day20 - 래퍼클래스(Wrapper class), Boxing&UnBoxing (0) | 2020.07.07 |
[필기정리]Day19 - equals(), clone() 등 (0) | 2020.07.06 |
[필기정리]Day18 - 전화번호 관리 프로그램 문제 06단계 (0) | 2020.07.03 |