[필기정리]Day21 - 문제 step3

SW/Java

2020. 7. 8. 13:16

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>();

- 기본자료형으로 올 수 없기 때문에 래퍼클래스로 만들어주는 것이다.

728x90