개인 공부 (23.07~

[Java] Generic(제네릭) 개념과 예시

Song쏭 2023. 8. 30. 16:51
실수를 방지하기 위한 제네릭.

T: 구체적인 하나의 타입 (클래스, 인터페이스, 메서드에서 일반적으로 사용)
?: 알 수 없는 모든 타입 (일반적으로 메서드 매개변수에서 사용)
? extends T: T의 하위 타입 (일반적으로 메서드 매개변수에서 사용)
? super T: T의 상위 타입 (일반적으로 메서드 매개변수에서 사용)

 

제네릭은 타입 형 변환에서 발생할 수 있는 문제점을 "사전"에 없애기 위해 만들어졌다.

"사전"이라고 하는 것은 실행 시에 예외가 발생하는 것을 처리하는 것이 아니라,

컴파일할 때 점검할 수 있도록 한 것을 말한다.

 

제네릭을 사용하지 않을 때는 아래와 같이 형 변환을 해야만 했다. (Object 클래스는 모든 클래스의 부모 클래스이기 때문에)

이렇게 될 경우 dto2의 인스턴스 변수의 타입이 StringBuilder인지 StringBuffer인지 혼동될 경우가 있다. 

그럴때는 instanceof를 사용하여 점검한다.

꼭 이렇게 타입을 점검해야하는 것일까. 

이런 단점을 보완하기 위해서 Java5부터 새롭게 추가된 것이 제네릭이라는 것이다.

String temp1 = (String)dto1.getObject();
StringBuffer temp2 = (StringBuffer)dto2.getObject();
StringBuilder temp3 = (StringBuilder)dto3.getObject();

제네릭을 사용하게되면 아래와 같이 간단해진다.

형 변환을 할 필요가 없어진 것이다.

해당 객체에 선언되어 있는 제네릭 타입은 각각 String, StringBuffer, StringBuilder이기 때문에

우리가 잘못된 타입으로 치환하면 컴파일 자체가 안된다.

실행 시에 다른 타입으로 잘못 형 변환하여 예외가 발생하는 일은 없어진 것이다.

String temp1 = dto1.getObject();
StringBuffer temp2 = dto2.getObject();
StringBuilder temp3= dto3.getObject();

이처럼 명시적으로 타입을 지정할 때 사용하는 것이 제네릭이다.

 

제네릭 클래스

클래스 옆에 예를들어 <E>라는 제네릭타입을 붙여 해당 클래스 내에서 사용할 수 있는 E 타입으로 일반화를했다.

 

제네릭 메서드

[접근제어자] <제네릭타입> [반환타입] [메서드명] ( [제네릭타입] [파라미터] ) {

 

}

 

public <T> T genericMethod(T o) {

}

 

매개변수 타입에 따라 T 타입이 결정된다.

즉, 클래스에서 지정한 제네릭 유형과는 별도로

메서드에서 독립적으로 제네릭 유형을 선언하여 쓸 수 있다.

 

제네릭이 사용되는 메서드를 정적메서드로 두고 싶은 경우

제네릭 클래스와 별도로 독립적인 제네릭이 사용되어야한다는 것이다.

 

제네릭에 ?가 있는 것은 무엇이지? ( <?> = 와일드카드 = 알 수 없는 타입)
public void wildcardStringMethod(WildcardGeneric<String> c) {
	String value = c.getWildcard();
    System.out.println(value);
}
public void wildcardStringMethod(WildcardGeneric<?> c){
	Object value = c.getWildcard();
    System.out.println(value);
}

메서드의 매개변수자리에 제네릭을 사용했다.

제네릭 타입 부분에

반드시 사용하는 String으로

제네릭 타입 객체만 받을 수 있게 만든 경우 해당 타입인 String밖에 못온다.

 

따라서 타입을 선언하는 부분에

?를 적어주면

어떤 타입이 제네릭 타입이 되더라도 상관없다는 것을 뜻한다.

 

?로 명시한 타입을 영어로는 wildcard  타입이라고 부른다.

 

wildcard는 메서드의 매개변수로만 사용하는 것이 좋다.

어떤 객체를 wildcard로 선언하고, 그 객체의 값은 가져올 수 있지만,

wildcard로 객체를 선언했을 때는 특정 타입으로 값을 지정하는 것은 불가능하다.

 

제네릭 선언에 사용하는 타입의 범위도 지정할 수 있다.

제네릭을 사용할 때 <> 안에는 어떤 타입이라도 상관없지만,

wildcard로 사용하는 타입을 제한할 수는 있다.

"?" 대신 "? extends 타입"으로 선택하는 것이다. 

"? extends 타입" = "Bounded Wildcards"

매개변수로 넘어오는 제네릭 타입의 경계를 지정하는 데 사용한다는 의미로 해석

 

 

타입제한을 걸기 위해서 제네릭이 있는 것이다.

1. 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다. (다른 타입으로 변수를 선언하면 바로 컴파일 오류가 뜬다.)

2. 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 반환해줄 필요가 없다. (instanceof 사용 안해도된다.)

즉, 관리하기 편하다!

 

 

 

* 자바의 신 교재와 구글링으로 공부한 것들을 기록 중