본문 바로가기

# 02/Java

[윤성우 열혈자바] 21-2. 제네릭의 기본 문법

반응형

다중 매개변수 기반 제네릭 클래스의 정의


class DBox<L, R> {

private L left;             // 왼쪽 수납 공간

private R right;            // 오른쪽 수납 공간


public void set(L o, R r) {

left = o;

right = r;

}


@Override

public String toString() {

return left + " & " + right;

}

}



public static void main(String[] args) {


DBox<String, Integer> box = new DBox<String, Integer>();

box.set("Apple", 25);

System.out.println(box);


}








타입 매개변수의 이름 규칙


일반적인 관례


한 문자로 이름을 짓는다.

대문자로 이름을 짓는다.




보편적인 선택


E        Element

K        Key

N        Number

T        Type

V        Value








기본 자료형에 대한 제한 그리고 래퍼 클래스


class Box<T> {

private T ob;


public void set(T o) {

ob = o;

}


public T get() {

return ob;

}

}


class PrimitivesAndGeneric {

public static void main(String[] args) {

Box<Integer> iBox = new Box<Integer>();

iBox.set(125);                    // 오토 박싱 진행

int num = iBox.get();                // 오토 언박싱 진행

System.out.println(num);

}

}


Box<int> box = new Box<int>();

-> 타입 인자로 기본 자료형이 올 수 없으므로 컴파일 오류 발생








다이아몬드 기호 <>


따라서 다음 문장을 대신하여,


Box<Apple> aBox = new Box<Apple>();




다음과 같이 쓸 수 있다.


Box<Apple> aBox = new Box<>();


참조변수 선언을 통해서 컴파일러가 사이에 Apple이 와야 함을 유추한다.








'매개변수화 타입'을 '타입 인자'로 전달


class Box<T> {

private T ob;


public void set(T o) {

ob = o;

}

public T get() {

return ob;

}

}


public static void main(String[] args) {

Box<String> sBox = new Box<>();

sBox.set("I am so happy.");


Box<Box<String>> wBox = new Box<>();

wBox.set(sBox);


Box<Box<Box<String>>> zBox = new Box<>();

zBox.set(wBox);


System.out.println(zBox.get().get().get());

}










제네릭 클래스의 타입 인자 제한하기


class Box<T extends number> { ... }


-> 인스턴스 생성 시 타입 인자로 Number 또는 이를 상속하는 클래스만 올 수 있음



class Box<T extends Number> {

private T ob;


public void set(T o) {

ob = o;

}

public T get() {

return ob;

}

}


public static void main(String[] args) {

Box<Integer> iBox = new Box<>();

iBox.set(24);


Box<Double> dBox = new Box<>();

dBox.set(5.97);


. . . .

}








타입 인자 제한의 효과


class Box<T> {

private T ob;


. . . .


public int toIntValue() {

return ob.intValue();                    // ERROR! - 컴파일 에러 발생!! - T 가 String 일 수도 있기 때문

}

}


class Box<T extends Number> {

private T ob;

. . . .


public int toIntValue() {

return ob.intValue();                    // OK!

}

}


이 효과가 타입 인자를 제한하는 실질적인 이유인 경우가 많다.(중요!!!!!!)








제네릭 클래스의 타입 인자를 인터페이스로 제한하기


interface Eatable { public String eat(); }


class Apple implements Eatable {

public String eat() {

return "It tastes so good!";

}

. . . .

}



class Box<T extends Eatable> {

T ob;


public void set(T o) { ob = o; }

public T get() {

System.out.println(ob.eat());                // Eatable로 제한하였기에 eat 호출 가능

return ob;

}

}








하나의 클래스와 하나의 인터페이스에 대해 동시 제한 가능!!


class Box<T extends Number & Eatable> { ... }


Number는 클래스 이름 Eatable은 인터페이스 이름 








제네릭 메소드의 정의


클래스 전부가 아닌 메소드 하나에 대해 제네릭으로 정의


class BoxFactory {

public static <T> Box<T> makeBox(T o) {

Box<T> box = new Box<T>();            // 상자를 생성하고,

box.set(o);                                    // 전달된 인스턴스를 상자에 담아서,

return box;                                   // 상자를 반환한다.

}

}




제네릭 메소드의 T는 메소드 호출 시점에 결정한다.


Box<String> sBox = BoxFactory.<String>makeBox("Sweet");

Box<Double> dBox = BoxFactory.<Double>makeBox(7.59);            // 7.59에 대해 오토 박싱 진행됨




다음과 같이 타입 인자 생략 가능


Box<String> sBox = BoxFactory.makeBox("Sweet");

Box<Double> dBox = BoxFactory.makeBox(7.59);                         // 7.59에 대해 오토 박싱 진행됨








제네릭 메소드의 제한된 타입 매개변수 선언


// <T extends Number>는 타입 인자를 Number를 상속하는 클래스로 제한함을 의미

public static <T extends Number> Box<T> makeBox(T o) {


. . . .


// 타입 인자 제한으로 intValue 호출 가능

System.out.println("Boxed data : " + o.intValule());

return box;

}




// 타입 인자를 Number를 상속하는 클래스로 제한

public static <T extends Number> T openBox(Box<T> box) {

// 타입 인자 제한으로 intValue 호출 가능


System.out.println("Unboxed data : " + box.get().intValue());

return box.get();

}



반응형