제네릭의 여러 형태 (제네릭 - 하)

김정용·2024년 2월 19일
5

궁금했던개념

목록 보기
6/12

Generic

"어렵고 어려운 제네릭".... 에 대해 다뤄본다.

제네릭-상 에 이어지는 글입니다.

Generic Method 👨‍💻

매개타입과 리턴 타입으로 타입파라미터를 갖는 메소드를 말한다.

선언방법

public <타입파라미터, ..> 리턴타입 메소드명(매개변수, ...) {
}

코드처럼 타입 앞에 <> 를 추가하고 타입 파라미터를 기술한다음, 리턴 타입, 매개 타입으로 타입 파라미터를 사용하면 된다.

public <T> Box<T> boxing(T t) {
}
Box<Integer> box = boxing(100);

boxing을 선언한 부분에서는 T 타입으로 선언을 하고, 사용시 Integer를 명시해줌으로써 정수를 함수 내에 넣게 된다.



제한된 타입 파라미터 🚧

타입 파라미터에 구체적인 타입을 제한하는 기능

예를 들어, 숫자를 연산하는 제네릭 메소드가 존재할때, 숫자형식의 타입 인스턴스만 가질 수 있게 제한하는 방법이다.

사용방법

public **<T extends 상위타입>** 리턴타입 메소드(매개변수, ...) {
}
public <T extends Number> int compare(T t1, T t2) {
	double v1 = t1.doubleValue();
	double v2 = t2.doubleValue();
	return Double.compare(v1, v2)
}

<T extends Number> 이 부분에 집중해 보자 !

T라는 타입 파라미터를 사용하지만 extends Numbers라는 부분을 통해 Number타입이나 Byte, Short, Integer, Long, Double 같은 Numbers를 상속받은 인스턴스만 사용 가능하게 된다.



와일드카드 타입 🃏

코드에서 ? 기호를 일반적으로 와일드카드라고 부른다.
보통 제한을 두고 싶지 않을때? 많이 사용하는 기호이다.

제네릭 타입을 매개값이나 리턴 타입으로 사용할때, 구체적인 타입 대신 와일드 카드를 사용한다.

아래와 같이 3가지 형태로 사용한다.


  1. 제네릭타입 <?> : Unbounded Wildcards (제한 없음)

    • 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.
  2. 제네릭 타입 <? extends 상위타입> : Upper Bounded Wildcards (상위 클래스 제한)

    • 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만 올 수 있다.
  3. 제네릭 타입 <? super 하위타입> : Lower Bounded Wildcards (하위 클래스 제한)

    • 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입이 올 수 있다.


예시

다음과 같은 관계로 클래스가 존재한다고 가정해보자.

위 3가지 형태를 해당 예시에 적용하면 다음과 같다.

  • Course<?> : 수강생은 모든 타입(Person, Worker, Student, HighStudent)이 될 수 있다.
  • Course<? extends Student> : 수강생은 Student와 HighStudent만 될 수 있다.
  • Course<? super Worker> : 수강생은 Worker와 Person만 될 수 있다.


제네릭 타입의 상속 👨‍💻

제네릭 타입 역시 상속이 가능하다.

public class ChildProduct<T, M> extends Product<T, M> {
}

위 코드는 Product<T, M> 을 상속받은 ChildProduct<T, M>을 보여준다.

특징

제네릭타입의 상속의 경우 특징이 한 가지 존재한다.

자식 제네릭 타입은 추가적으로 타입 파라미터를 가질 수 있다.

public class ChildProduct<T, M, C> extends Product<T, M> {
}

부모에게 상속 받은 T, M 뿐 아니라 위와 같이 새로운 타입 파라미터를 추가할 수 있다.



제네릭 타입의 구현 👨‍💻

만약 제네릭 인터페이스를 구현한 클래스가 있다고 가정하자.
해당 클래스는 무조건 제네릭 타입이여야 한다.

public interface Storage<T> {
	void add(T item, int index);

	T get(int index);
}

위와 같이 Stroage라는 제네릭 인터페이스가 있다.

public class StorageImpl<T> implements Storage<T> {
	private T[] array;

	public StorageImpl(int capacity) {
		array = (T[]) (new Object[capacity]);
	}

	@Override
	public void add(T item, int index) {
		array[index] = item;
	}

	@Override
	public T get(int index) {
		return array[index];
	}
}

그렇다면, Storage를 구현한 StorageImpl 클래스도 위와 같이 제네릭 타입이여야한다.



마치며

자바 심화부분 특히 최근 자바에 추가된 내용들이 나오면서 (+스트림, 람다) 새롭게 공부해야할 내용들이 많아지는 느낌이다. 새롭게 추가되었다고 무조건적으로 좋은 기술들은 아니기에 장점은 극대화하고 단점은 최소화하며 내 코드에 녹이는 연습이 필요하다. 😏

코드 예시 출처 : 이스트소프트 부트캠프 - 오르미

profile
누군가의 롤모델이 될 때까지😇

0개의 댓글