일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- ArrayList
- try-catch
- 바이트 단위 스트림
- 변수
- Java
- FileInputStream
- throws
- SSR
- MPA
- 자료형
- node.js
- 보조 스트림
- 인터페이스
- 상속
- 졸리다
- 새벽공부
- 배열
- 초보개발자
- 파이팅
- 예외 처리
- exception
- 자바
- 코린이
- 인스턴스
- 문자 단위 스트림
- 다형성
- 코딩
- 메서드
- 백엔드
- 개발자
- Today
- Total
SHUSTORY
12-1 제네릭 본문
제네릭
프로그램에서 변수를 선언할 때 모든 변수는 자료형이 있으며, 메서드에서 매개변수를 사용할 때도 마찬가지이다.
대부분은 하나의 자료형으로 구현하지만, 어떤 값이 하나의 참조 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록
프로그래밍 하는 것을 '제네릭 프로그래밍' 이라고 한다.
제네릭 프로그램은 참조 자료형이 변환될 때 이에 대한 검증을 컴파일러가 하므로 안정적이다.
다이아몬드 연산자 < >
자바 7부터는 제네릭 자료형의 클래스를 생성할 때 생성자에 사용하는 자료형을 명시하지 않을 수 있다.
여기에서 < >를 다이아몬드 연산자라고 한다.
선언된 자료형을 보고 생략된 부분이 String임을 컴파일러가 유추할 수 있기 때문에 생성 부분에서는 생략 가능하다.
자료형 매개변수 T와 static
static 변수나 메서드는 인스턴스를 생성하지 않아도 클래스 이름으로 호출할 수 있다.
static 변수는 인스턴스 변수가 생성되기 이전에 생성된다. 또한 static 메서드에서는 인스턴스 변수를 사용할 수 없다.
T의 자료형이 정해지는 순간은 제네릭 클래스의 인스턴스가 생성되는 순간이다.
따라서 T의 자료형이 결정되는 시점보다 빠르기 때문에 static 변수의 자료형이나 static 메서드내부 변수의 자료형으로 T를 사용할 수 없다.
자료형 매개변수로 T 외에 다른 문자도 사용할 수 있다. E는 element, K는 key, V는 value를 의미한다.
참고로 꼭 이런 문자를 사용해야 하는 것은 아니며 A, B 등 원하는 문자를 사용해서 정의할 수도 있다.
제네릭에서 자료형 추론하기
자바10부터는 지역 변수에 한해서 자료형을 추론할 수 있으며, 이는 제네릭에도 적용된다.
String을 자료형 매개변수로 사용한 ArrayList 선언 코드를 다음처럼 바꿀 수 있다.
생성되는 인스턴스를 바탕으로 list의 자료형이 ArrayList<String>임을 추론할 수 있기 때문이다.
물론 list가 지역 변수로 선언되는 경우만 가능하다.
제네릭 예제_3D 프린터
3D 프린터는 재료를 가지고 입체 모형을 만드는 일을 한다.
재료의 종류에는 파우더와 플라스틱이 있다고 하자.
먼저 Powder 클래스와 Plastic 클래스를 정의하자.
제네릭의 필요성
먼저 제네릭을 사용하지 않고 3D 프린터 클래스 코드를 작성해보자.
그런데 재료가 바뀔 때마다 위와 같이 동일한 기능의 프린터를 새로 만드는 것은 비효율적이다.
이런 경우 어떤 재료든 쓸 수 있도록 material 변수의 자료형을 Object로 사용할 수 있다.
Object는 모든 클래스의 최상위 클래스이므로 모든 클래스는 Object로 변환할 수 있기 때문이다.
material 변수의 자료형을 Object로 선언한 3D프린터에 파우더를 재료로 사용하면 다음과 같은 코드를 구현할 수 있다.
setMaterial( ) 메서드를 활용하여 Powder 재료로 선택할 때는 매개변수 자료형이 Object이므로 자동으로 형 변환이 된다.
하지만 반환형이 Object인 getMaterial( ) 메서드로 Powder 자료형 변수를 반환받을 때는 반드시 형 변환을 해 줘야 한다.
즉 어떤 변수가 여러 참조 자료형을 사용할 수 있도록 Object 클래스를 사용하면 다시 원래 자료형으로 반환해 주기 위해 매번 형 변환을 해야 하는 번거로움이 있는데, 이를 해결해 주는 방식이 제네릭이다.
여러 참조 자료형이 쓰일 수 있는 곳에 특정한 자료형을 지정하지 않고, 클래스나 메서드를 정의한 후 사용하는 시점에 어떤 자료형을 사용할 것인지 지정하는 방식이다.
제네릭 클래스 정의하기
코드를 보면 여러 자료형으로 바꾸어 사용할 material 변수의 자료형을 T라고 썼다.
이 때 T를 자료형 매개변수라고 부른다.
클래스 이름을 GenericPrinter<T>라고 정의하고 나중에 클래스를 사용할 때 실제 사용할 자료형을 지정한다.
클래스의 각 메서드에서 자료형이 필요한 부분에는 모두 T문자를 사용하여 구현한다.
10-12행의 getMaterial( ) 메서드는 T 자료형 변수 material을 반환한다.
메서드 선언부나 메서드의 매개변수로 자료형 매개변수 T를 사용한 메서드를 제네릭 메서드라고 한다.
제네릭 클래스 사용하기
파우더가 재료인 프린터는 다음과 같이 선언하여 생성한다.
T로 정의한 클래스 부분에 Powder형을 넣어 주고, T형 매개변수가 필요한 메서드에 Powder 클래스를 생성하여 대입한다.
GenericPrinter<Powder>에서 어떤 자료형을 사용할지 명시했으므로 getMaterial( ) 메서드에서 반환할 때 형 변한을 하지 않는다.
이렇게 실제 제네릭 클래스를 사용할 때 T 위치에 사용한 Powder형을 '대입된 자료형'이라 하고,
Powder를 대입해 만든 GenericPrinter<Powder>를 '제네릭 자료형' 또는 '매개변수화된 자료형' 이라고 한다.
제네릭으로 구현하면 형 변환을 하지 않아도 되는 이유는 무엇일까?
제네릭 클래스를 사용하면 컴파일러는 일단 대입된 자료형이 잘 쓰였는지 확인한 후, class 파일을 생성할 때 T를 사용한 곳이 지정된 자료형에 따라 컴파일하기 때문이다.
5행과 11행에서 사용할 참조 자료형을 지정하여 GenericPrinter 클래스를 생성한다.
만약 새로운 재료가 추가되면 추가된 재료 클래스를 만들고 T 대신 해당 클래스를 대입하여 GenericPrinter를 생성한다.
T 자료형에 사용할 자료형을 제한하는 <T extends 클래스>
필요에 따라 제네릭 클래스에서 T 자료형에 사용할 자료형에 제한을 둘 수 있다.
3D 프린터 예제를 통해 설명하자면, 파우더와 플라스틱만이 재료로 사용 가능한 프린터에서 물을 재료로 쓰겠다고 할 때, 이런 일을 방지하기 위해 사용할 클래스에 자료형 제한을 두는 방식으로 extends 예약어를 사용하는 것이다.
Material 클래스를 추상 클래스로 정의하고, Powder와 Plastic 클래스가 이를 상속받고,
제네릭프린터 클래스에서 public class GenericPinter<T extends Material> {...} 형태로 사용한다.
위와 같이 프로그램을 구현한 경우 Material 클래스를 상속받지 않은 Water 클래스를 자료형에 사용하면 오류가 발생한다.
T 위치에 특정 인터페이스를 구현한 클래스만 사용하려는 경우에도 extends 예약어를 사용할 수 있다.
<T extends Material>로 선언하면 제네릭 클래스를 사용할 때 상위 클래스 Material에서 선언한 메서드도 사용할 수 있다.
컴파일할 때 내부적으로 T 자료형이 Object가 아닌 Material로 변환되기 때문이다.
'프로그래밍 > JAVA' 카테고리의 다른 글
12-3 List 인터페이스 (0) | 2023.03.08 |
---|---|
12-2 컬렉션 프레임워크 (0) | 2023.03.08 |
11-3 Wrapper 클래스 (0) | 2023.03.06 |
11-2 String 클래스 (0) | 2023.03.06 |
11-1 Object 클래스 (0) | 2023.03.06 |