1. Generic(제네릭) 클래스의 타입 인자 제한하는 방법과 효과는?
class 클래스명<타입인자 extends 부모클래스/인터페이스> {...}
class 클래스명<타입인자 extends 부모클래스 & 인터페이스> {...}
class Box<T extends Number> {...} // super class
class Box<T extends Eatable> {...} // interface
class Box<T extends Number & Eatable > //super class & interface 동시 제한
// 인스턴스 생성시 타입 인자로 T가 Number이거나 Number를 상속하는 클래스만 올 수 있음!
// 인스턴스 생성시 타입 인자로 T가 Eatable이거나 Eatable을 구현하는 클래스만 올 수 있음!
// 위의 두 경우를 모두 만족하는 경우에만 올 수 있음(generic에서만 가능, 일반 클래스에선 불가!)
2. 아래의 결과가 나오도록 프로그래밍을 완성 하시오.
class DDBoxDemo {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
ddbox.set(box1, box2);
System.out.println(ddbox);
}
}
/*
==================
Apple & 25
Orange & 33
*/
class DBox<T1, T2> {
private T1 ob1;
private T2 ob2;
public void set(T1 ob1, T2 ob2) {
this.ob1 = ob1;
this.ob2 = ob2;
}
@Override
public String toString() {
return ob1 + " & " + ob2;
}
}
class DDBox<T1, T2> {
private T1 ob1;
private T2 ob2;
public void set(T1 ob1, T2 ob2) {
this.ob1 = ob1;
this.ob2 = ob2;
}
@Override
public String toString() {
return ob1 + "\n" + ob2;
}
}
public class DDBoxDemoMain {
public static void main(String[] args) {
DBox<String, Integer> box1 = new DBox<>();
box1.set("Apple", 25);
DBox<String, Integer> box2 = new DBox<>();
box2.set("Orange", 33);
DDBox<DBox<String, Integer>, DBox<String, Integer>> ddbox = new DDBox<>();
ddbox.set(box1, box2);
System.out.println(ddbox);
}
}
3. 아래와 같이 출력값이 나오도록 프로그래밍 하시오.
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(99);
Box<Integer> box2 = new Box<>();
box2.set(55);
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2);
System.out.println(box1.get() + " & " + box2.get());
}
/*
==========
99 & 55
55 & 99
*/
class Box<T extends Number> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class IntegerSwapBoxMain {
// 처음의 시도
// private static void swapBox(Box<Integer> box1, Box<Integer> box2) {
// Box<Integer> tempBox = new Box<>();
// tempBox.set(box1.get());
// box1.set(box2.get());
// box2.set(tempBox.get());
// }
// 이게 더 좋은 방법인것같다!
private static <T> void swapBox(Box<T> box1, Box<T> box2) {
T temp = box1.get();
box1.set(box2.get());
box2.set(temp);
}
/*
Box<T>를 Box<T extends Number>로 제한한 경우라면 이렇게 사용하는게 맞는건가?
private static <T extends Number> void swapBox(Box<T> box1, Box<T> box2) {
T temp = box1.get();
box1.set(box2.get());
box2.set(temp);
}
*/
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(99); //오토 박싱
Box<Integer> box2 = new Box<>();
box2.set(55); //오토 박싱
System.out.println(box1.get() + " & " + box2.get());
swapBox(box1, box2);
System.out.println(box1.get() + " & " + box2.get());
}
}
4. 지네릭 메소드에 대하여 설명하시오.
class BoxFactory{
public static <T> Box<T> makeBox(T o) { //static이 옴
/*
<T> → 함수앞 generic: generic method임을 의미
(그 이상의 의미 이상도 이하도 아님, 이자리의 <T>는 선언해주고 사라짐)
Box<T> → 리턴타입
makeBox(T o) → 함수
*/
Box<T> box = new Box<T>(); // 상자를생성하고,
box.set(o); // 전달된인스턴스를상자에담아서,
return box; // 상자를반환한다.
}
}
Generic의 타입 결정 시점
- class: T가 인스턴스 생성시에 결정된다.
- method: T가 메소드가 호출될 때 결정된다.
- 전체 예문 Box<String> sBox = BoxFactory.makeBox("sweet"); 수업시간 코딩
함수 호출시에 타입 인자는 생략가능!!
Box<String> sBox = BoxFactory.<String>makeBox("Sweet");
Box<Double> dBox = BoxFactory.<Double>makeBox(7.59); //오토 박싱
Box<String> sBox = BoxFactory.makeBox("Sweet");
Box<Double> dBox = BoxFactory.makeBox(7.59); //오토 박싱
-
여기서 = 의 오른쪽에 있는 <>가 생략 가능한 이유는 = 왼쪽의 Box<String>과 같은 타입을 보고
결정하는것이 아닌 ("Sweet")와 같은 전달되는(괄호)안의 인자를 보고 결정하는 것이다.
그렇게 어떤 타입을 적용할 지를 위의 방법으로 컴파일러가 대신 해주는 것이다.
전달되는 인자가 double타입과 같은 primitive 타입인 경우는 Double타입과 같이
오토박싱 하여 넣어준다. → 오토 박싱등을 포함한 이 모든 과정을 컴파일러가 대신해줌
5. 와일드 카드와 상한 제한, 하한 제한에 대하여 설명하시오.
와일드 카드
-
자바에서 추천하는 문법
점점 <T>를 복잡하게 만들어야 하는데 Generic 보다 <?>와일드카드를 복잡하게 만드는게 코드적으로 간결해 진다. (보기에 간단, 간결 큰 이유는 없으나 문서에서 권고 되어질 정도로 선호)
-
Generic과 기능적으로는 동일한 문법(점차 와일드 카드를 기반으로 코드를 작성하게 될 것)
그럼 왜 둘 다 제공? 같이 합쳐지면 막강한 파워를 발휘할 수 있다. 그렇게 같이 사용하기 위해 만들어진 문법이다.
? 는 무엇을 넣어도 받아 주겠다는 선언. Generic의 T를 좀 더 간략하게 표현한 것
// 와일드 카드
public static void peekBox(Box<?> box) {
System.out.println(box);
}
// 지네릭으로 표현
public static <T> peekBox(Box<T> box) {
System.out.println(box);
}
상한 제한된 와일드카드(Upper-Bounded Wildcards)
- Number가 최고 상한이다 라고 해서 상한 제한이라고 표현 (Number 또는 Number를 상속하는 sub클래스 여야 함)
public static void peekBox(Box<? extends Number> box) {
System.out.println(box);
}
하한 제한된 와일드카드(Lower-Bounded Wildcards)
-
지네릭에는 super 키워드 없다.
Integer 위에만 올 수있다. (해석은 거꾸로! Integer → 위에 → ? ) 그래서 하한 제한
(Integer 또는Integer가 상속하는 클래스 이어야함)
= 즉, 위 메소드의 인자로 전달 가능한 인스턴스는 Box<Integer>, Box<Number>, Box<Object>로 제한 됨
public static void peekBox(Box<? super Integer> box) {
System.out.println(box);
}
6. 아래가 에러가 나는 이유를 설명하시오.
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public get() { return ob; }
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class BoxHandler {
public static void outBox(Box<? extends Toy> box) {
Toy t = box.get(); // 박스에서 꺼내기
System.out.println(t);
}
public static void inBox(Box<? super Toy> box, Toy n) {
box.set(n); // 박스에 넣기
Toy myToy = box.get (); // 꺼내는 것 ! 에러!!
}
}
class BoundedWildcardUsage2 {
public static void main(String[] args) {
Box<Toy> box = new Box<>();
BoxHandler.inBox(box, new Toy());
BoxHandler.outBox(box);
}
}
public static void inBox(Box<? super Toy> box, Toy n) {
box.set(n); // 넣는 것! OK!
Toy myToy = box.get(); // 꺼내는 것! Error!
}
? super Toy는 Toy 위에 것만 오라는 것! 그래서 하한 제한이다.
<? super Toy> 에는 Toy, Plastic 등 어떤게 들어올지 아직 모르기 때문에 이미 Plastic이 들어오는 경우를 가정해보면 Toy myToy = box.get(); 이 자식 = 부모 가 되어 자신을 참조하고 있지 않아서 다른 경우는 볼 필요도 없이 컴파일러는 에러를 표시한다.
7. 아래와 같이 메소드 오버로딩이 되지 않는 이유는?
// 다음 두 메소드는 오버로딩 인정 안됨.
public static void outBox(Box<? extends Toy> box) {...}
public static void outBox(Box<? extends Robot> box) {...}
Generic 안에 있는 매개변수 타입에 대해서는 오버로딩이 안된다.
<...>안에 있는 내용은 타입이 정해지면 컴파일 과정에서 지워지기 때문에 사실상 두 문장은 똑같은 거여서 오버로딩 자체 될 수가 없다. 따라서 컴파일러가 이러한 형태의 메소드 오버로딩을 허용하지 않는다.
8. 아래의 결과가 나오도록 프로그래밍을 완성 하시오.
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24);
Box<String> box2 = new Box<>();
box2.set("Poly");
if(compBox(box1, 25))
System.out.println("상자 안에 25 저장");
if(compBox(box2, "Moly"))
System.out.println("상자 안에 Moly 저장");
System.out.println(box1.get());
System.out.println(box2.get());
}
/*
=======
24
Poly
*/
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class CompareBoxMain {
private static <T> boolean compBox(Box<T> ob1, T ob2) {
if(ob1.get().equals(ob2)) {
return true;
}else
return false;
}
public static void main(String[] args) {
Box<Integer> box1 = new Box<>();
box1.set(24); //오토 박싱
Box<String> box2 = new Box<>();
box2.set("Poly"); //오토 박싱
if(compBox(box1, 25))
System.out.println("상자 안에 25 저장");
if(compBox(box2, "Moly"))
System.out.println("상자 안에 Moly 저장");
System.out.println(box1.get());
System.out.println(box2.get());
}
}
9. 콜렉션 프레임워크란?
- 자료구조 및 알고리즘을 구현해 놓은 일종의 라이브러리! (라이브러리: .class 모임)
- 데이터들(자바에서 객체) 좀더 효율적으로 빠르게 관리 Control 하기 위한 방법(read, write, remove, store...)
- 제네릭 기반으로 구현이 되어있다. 써먹을 때도 지네릭 기반으로 써먹음
- Set<E>, List<E>, Queue<E>, Map<K, V> 이있다.