순간을 기록으로

[JAVA] 제네릭1 본문

Development/JAVA

[JAVA] 제네릭1

luminous13 2021. 11. 25. 18:31

제네릭 이전의 코드의 단점

1.클래스가 자료형에 의존적이게 된다.

 

2.인스턴스를 꺼낼 때 형변환을 해야한다.

Apple ap = (Aplle)aBox.get();
Orange og = (Orange)oBox.get();

 

3.실수가 컴파일 과정에서 발견되지 않는다.

aBox.set("Apple");	// 문자열 "Apple" 담았는데 실수다
oBox.set("Orange");	// 문자열 "Orange" 담았는데 실수다

 

제네릭 기반 클래스 정의

class Box<T> {
	private T ob;
    public void set(T o) {
    	ob = o;
    }
    public T get() {
    	return ob;
    }
}

제네릭의 등장으로 자료형에 의존적이지 않은 클래스를 정의할 수 있고 앞에서 본 문제점을 해결할 수 있다.

제네릭의 <T>는 인스턴스 생성 시 T의 자료형을 결정한다.

 

용어정리

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

 

  • <T>: 타입 매개변수(Type Parameter)
  • Apple: 타입 인자(Type Argument)
  • Box<Apple>: 매개변수화 타입(Parameterized Type), 제네릭 타입(Generic Type)

 

다중 매개변수 제네릭  클래스

class DBox<L, R> {	// 다중 매개변수 기반 제네릭 클래스
	private L left;
    private R right;
    
    public void set(L o, R r) {
    	left = o;
        right = r;
    }
}

 

타입 인자의 규칙1: 기본자료형은 불가능

Box<Apple>처럼 '매개변수화 타입'을 만들 때 '타입인자'는 기본 자료형이 불가능하다. 즉 Box<int>는 사용할 수 없다.

그러므로 래퍼클래스를 사용하게 되고 상황에 따라 자동으로 박싱과 언박싱이 진행된다.

 

 

타입 인자의 규칙2: 생략가능

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

위 두 문장은 같다. 즉 컴파일러는 참조변수의 선언을 보고 오른쪽의 타입인자를 생략해도 추론할 수 있다.

 

타입 인자의 규칙3: 매개변수화 타입을 타입인자로 사용할 수 있다.

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

다음과 같이 '매개변수화 타입'을 '타입 인자'로 사용할 수 있다. 

 

타입 인자 제한하기1: extends 클래스 

때로는 담고 싶은 것을 제한하고 싶을 수도 있다. 그럴때는 'extends'를 사용한다.

class Box<T enxtends Number>{...}

다음과 같이 사용하면 타입 인자로 사용할 수 있는 것은 Number와 Number을 상속하는 하위 클래스만 가능하다.\

 

 

타입 인자를 제한함으로써 얻는 효과: 특정 메소드 호출 가능

class Box<T extends Number> {
	private T ob;
    ...
    public int toIntValue() {
    	return ob.intValue();	// 가능
    }
}

다음과 같이 타입인자를 extends Number로 제한을 하면 T는 적어도 Number 이거나 Number을 상속하는 클래스이므로 ob 객체는 intValue() 메소드를 호출할 수 있다.

 

타입인자 제한하기2: extends 인터페이스

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

Eatable을 인터페이스라고 한다면 다음과 같이 인터페이스로 타입 인자를 제한할 수있다.

 

또한 타입인자는 클래스와 인터페이스로 동시에 제한 할 수 있다.

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

 

제네릭 메소드

클래스 전체가 아닌 일부 메소드에 대해서만 제네릭의 정의할 수 있다. static과 관련이 없다.

 

public static <T> Box<T> makeBox(T o) {...}	// 제네릭 메소드

맨 앞의 <T>는 제네릭 메소드를 알려주는 표시이다.

 

제네릭 클래스와 제네릭 메소드의 자료형 결정 시기

제네릭 클래스의 경우 인스턴스 생성시 자료형이 결정된다.

반면, 제네릭 메소드의 경우 메소드 호출시에 자료형이 결정된다.

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

컴파일러는 메소드 이름앞 <String>을 보고 제네릭 메소드의 자료형을 결정한다. 하지만 현재 컴파일러는 발전되어 참조변수의 형을 보고 제네릭 메소드형을 추론할 수 있다. 따라서 아래와 같이 생략할 수 있다.

 

제네릭 메소드의 타입인자 제한하기: extends

public static <T extends Number> Box<T> makeBox(T o) {	// 제한된 타입 인자
	Box<T> bo = new Box<T>();
    box.set(o);
    
    System.out.println("Boxed data: " + o.intValue());	// 타입인자 제한 효과
    return box;
    }
}

제네릭 메소드도 제네릭 클래스와 같이 타입인자를 제한할 수 있다. 그리고 그 효과도 같다.

Comments