Q. step3
문제1. 제네릭 메소드에 매개변수로 배열을 전달하는 형태로 정의 및 호출해 보자.
A.
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);
}
}
문제2. 다음의 메소드 정의를 보면서 매개변수로 전달될 수 있는 대상의 범위를 정리해 보자. public void hiMethod(Apple param) { ... }
A. 매개변수의 자료형이 Apple이거나,
Apple 인스턴스 또는 Apple을 상속하는 인스턴스의 참조 값이 매개 변수에 전달될 수 있다.
문제3. 다음의 메소드 정의를 보면서 매개변수로 전달될 수 있는 대상의 범위를 정리해 보자.
public void onMethod(FruitBox<Fruit> param) { ... }
A.
우선 매개변수 선언에서 보이는 그대로
FruitBox<Fruit> 인스턴스의 참조 값이 전달 대상이 될 수 있다.
Fruit
↑
Apple
class Apple extends Fruit
이렇듯 Apple 클래스가 Fruit 클래스를 상속하는 경우에,
FruitBox<Apple> 인스턴스의 참조 값이 위의 onMethod의 매개변수에 전달될 수 있겠는가?
정답은 No! 우리가 앞서 배운 상속의 관점에서 보면 Yes라고 답을 하는 것도 무리는 아니니 말이다.
하지만 Fruit과 Apple이 상속관계에 놓여있다고 해서
FruitBox<Fruit>과 FruitBox<Apple>이 상속관계에 놓이는 것은 아니다.
상속관계에 놓이려면 클래스가 정의되는 과정에서 키워드 extends를 통해서 상속됨이 명시되어야 한다.
그런데 FruitBox<Fruit>와 FruitBox<Apple>가 키워드 extends를 통해서 명시되는 관계는 아니지 않은가?
FruitBox<Apple> 인스턴스의 참조 값도 인자로 전달받을 수 있는 매개변수의 선언은 어떻게 해야 할까?
자바는 이를 위해서 와일드 카드를 이용한 자료형의 명시를 허용한다.
FruitBox<? extends Fruit> box1 = new FruitBox<Fruit>();
FruitBox<? extends Fruit> box2 = new FruitBox<Apple>();
위의 <? extends Fruit>가 의미하는 바는 "Fruit을 상속하는 모든 클래스"이다.
즉, 자료형을 결정 짓는 제네릭 매개변수 T에 Fruit클래스를 포함하여,
Fruit을 상속하는 클래스면 무엇이든 올 수 있음을 명시하는 것이다.
따라서 참조변수 box1과 box2는 다음의 형태로 생성되는 인스턴스면 무엇이든 참조가 가능하다.
new FruitBox<'Fruit 클래스, 또는 Fruit을 상속하는 클래스의 이름'>()
//Tip : 와일드 카드 - 이름 또는 문자열에 제한을 가하지 않음을 명시하는 용도로
사용되는 특별한 기호를 말한다.
문제4. 자료형을 결정짓는 제네릭 매개변수 T에 Fruit클래스를 포함하여,
Fruit을 상속하는 클래스면 무엇이든 올 수 있음을 명시할려면?
A.
FruitBox<? extends Fruit> box1 = new FruitBox<Fruit>();
문제5. 문제4에 해당하는 예제 소스코드를 작성하시오.
A.
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=box.pullOut();
fruit.showYou();
}
public static void main(String[] args)
{
FruitBox<Fruit> box1=new FruitBox<Fruit>();
box1.store(new Fruit());
FruitBox<Apple> box2=new FruitBox<Apple>();
box2.store(new Apple());
openAndShowFruitBox(box1);
openAndShowFruitBox(box2);
}
}
문제6. 전달되는 자료형에 상관없이 FruitBox<T>의 인스턴스를 참조하려면?
A. 아래와 같이 참조변수 선언하면 된다.
FruitBox<?> box;
FruitBox<? extends Object> box; // 둘 다 가능
문제7. FruitBox<T>가 인스턴스를 참조하되,
T가 Apple 클래스 또는 Apple 클래스가 직간접적으로 상속하는 클래스인 경우에만 참조할 수 있게 하려면?
A.
FruitBox<? super Apple> boundedBox;
extends는 다음의 의미로 사용된다.
"~을 상속하는 클래스라면 무엇이든지"
super는 다음의 의미로 사용이 된다.
"~이 상속하는 클래스라면 무엇이든지"
위에 선언된 참조변수 boundedBox는 FruitBox<T>의 인스턴스를 참조하되,
T가 Apple 클래스 또는 Apple 클래스가 직간접적으로 상속하는 클래스인 경우에만 참조할 수 있다.
예를 들어서 Apple 클래스가 Fruit 클래스를 상속하는 구조를 갖는다면(Apple extends Fruit),
위의 boundedBox가 참조할 수 있는 인스턴스의 자료형은 다음 세 가지이다.
FruitBox<Object>, FruitBox<Fruit>, FruiBox<Apple>
ex)
class Fruit
{
public String toString()
{
return "과일";
}
}
class Apple extends Fruit
{
public String toString()
{
return "사과";
}
}
class RedApple extends Apple
{
public String toString()
{
return "빨간 사과";
}
}
class FruitBox<T>
{
T item;
public void store(T item)
{
this.item = item;
}
public T pullOut()
{
return item;
}
}
public class Test {
public static void show(FruitBox<? super Apple> box)
{
System.out.println(box.pullOut());
}
public static void main(String[] args) {
FruitBox<Apple> fbox1 = new FruitBox<Apple>();
fbox1.store(new Apple());
show(fbox1);
FruitBox<Fruit> fbox2 = new FruitBox<Fruit>();
fbox2.store(new Fruit());
show(fbox2);
FruitBox<RedApple> fbox3 = new FruitBox<RedApple>();
fbox3.store(new RedApple());
// show(fbox3); // 에러
}
}
문제8. 제네릭 클래스 A를 상속하려면?
A.
class AAA<T>
{
T itemAAA;
}
class BBB<T> extends AAA<T>
{
T itemBBB;
}
이렇게 상속이 되면, 하나의 자료형 정보로 인해서 AAA의 자료형과 BBB의 자료형이 모두 결정된다.
즉, 다음과 같이 문장을 구성하면, T가 각각 String과 Integer로 대체되어 인스턴스가 생성된다.
BBB<String> myString = new BBB<String>();
BBB<Integer> myInteger = new BBB<Integer>();
문제9. AAA<T>클래스의 T를 지정해서 상속할려면?
A.
class BBB extends AAA<String>
{
int itemBBB;
}
물론 위의 BBB 클래스는 제네릭으로 정의될 수도 있다.
그러나 제네릭이 아니어도 된다는 것을 강조하기 위해서 일반 클래스로 정의하였다.
문제10. 인터페이스를 제네릭으로 정의해보자.
A.
interface MyInterface<T>
{
public T myFunc(T item);
}
interface AAA<T>
{
void aaa(T item);
}
문제11. 인터페이스를 구현하여 클래스를 정의하는 방식 두 가지
A.
① 다음과 같이 T를 그대로 유지하는 방식
class MyImplement<T> implements MyInterface<T>
{
public T myFunc(T item)
{
return item;
}
}
② 다음과 같이 T의 자료형을 결정하는 방식
class MyImplement implements MyInterface<String>
{
public String myFunc(String item)
{
return item;
}
}
주의해야 할 사실은 위의 클래스 정의와 같이 T의 자료형이 String으로 결정되면,
MyInterface<T>의 메소드 myFunc를 구현할 때에도 T가 아닌 String으로 명시해야 한다는 점이다.
* 기본자료형의 이름은 제네릭 클래스의 인스턴스 생성에 사용될 수 없다.
즉, 다음의 형태로는 인스턴스의 생성이 불가능하다.
FruitBox<int> fb1 = new FruitBox<int>();
FruitBox<double> fb2 = new FruitBox<double>();
- 기본자료형으로 올 수 없기 때문에 래퍼클래스로 만들어주는 것이다.
'SW > Java' 카테고리의 다른 글
[필기정리]Day22 - 문제 전화번호부 단계 7 (0) | 2020.07.09 |
---|---|
[필기정리]Day22 - 컬렉션 프레임워크 (0) | 2020.07.09 |
[필기정리]Day21 - 문제 step2 (0) | 2020.07.08 |
[필기정리]Day21 - 문제 step1 (0) | 2020.07.08 |
[필기정리]Day21 - Generic(제네릭) (0) | 2020.07.08 |