목차
제네릭(Generic)
1. 제네릭 이란?
제네릭 프로그래밍은 자바에서 타입을 일반화하여 재사용 가능한 코드를 작성하는 기법입니다. 제네릭을 사용하면 클래스, 인터페이스, 메서드 등에 대해 특정 타입을 지정하지 않고, 다양한 타입에서 동작하도록 할 수 있습니다.
제네릭 자료형 정의
- 클래스에서 사용하는 변수의 자료형이 여러개 일수 있고, 그 기능(메서드)은 동일한 경우 클래스의 자료형을 특정하지 않고 추후 해당 클래스를 사용할 때 지정 할 수 있도록 선언하는 것
- 실제 사용되는 자료형의 변환은 컴파일러에 의해 검증되므로 안정적인 프로그래밍 방식
- 컬렉션 프레임워크에서 많이 사용되고 있음
제네릭 프로그래밍의 주요 이점
- 타입 안정성: 컴파일 시 타입을 검사하여 런타임 에러를 줄일 수 있다.
- 코드 재사용성: 다양한 타입을 처리할 수 있는 일반화된 코드를 작성할 수 있다.
- 유지보수성: 타입 캐스팅을 줄여 가독성과 유지보수성을 향상시킨다.
2. 시나리오 코드 1 - 제네릭 미사용 (Object 클래스 활용)
public class Plastic {
@Override
public String toString() {
return "재료는 플라스틱 입니다.";
}
}
public class Powder {
@Override
public String toString() {
return "재료는 파우더 입니다.";
}
}
public class ThreeDPrinter {
Plastic material;
// get, set
public Plastic getMaterial() {
return material;
}
public void setMaterial(Plastic material) {
this.material = material;
}
}
public class ThreeDPrinter2 {
Powder material;
// get, set
public Powder getMaterial() {
return material;
}
public void setMaterial(Powder material) {
this.material = material;
}
}
/**
* 컴파일 시점에 material 데이터 타입으로는<br>
* 모든 클래스가 될 수 있다.
*/
public class ThreeDPrinter3 {
Object material;
// get, set
public Object getMaterial() {
return material;
}
public void setMaterial(Object material) {
this.material = material;
}
}
public class MainTest1 {
public static void main(String[] args) {
ThreeDPrinter dPrinter1 = new ThreeDPrinter();
dPrinter1.setMaterial(new Plastic());
System.out.println(dPrinter1.material);
// 위 ThreeDPrinter 한계는 재료가 플라스틱에 종속 되어있다.
// 하지만 사용자 입장에서 재료를 파우더로 변경한다면
// 코드의 수정이나 새로운 클래스가 필요하다
System.out.println("------------------");
ThreeDPrinter2 dPrinter2 = new ThreeDPrinter2();
dPrinter2.setMaterial(new Powder());
System.out.println(dPrinter2.material);
System.out.println("------------------");
ThreeDPrinter3 dPrinter3 = new ThreeDPrinter3();
dPrinter3.setMaterial(new Plastic());
System.out.println(dPrinter3.material);
System.out.println("------------------");
ThreeDPrinter3 dPrinter3_2 = new ThreeDPrinter3();
dPrinter3_2.setMaterial(new Powder());
System.out.println(dPrinter3_2.material);
Plastic plastic01 = (Plastic) dPrinter3.getMaterial(); // 다운 캐스팅
Powder powder01 = (Powder) dPrinter3.getMaterial(); // 오류나는 코드가 된다.
}
}
3. 시나리오 코드 2 - 제네릭 활용
package ch02;
/**
* 제네릭이란?<br>
* 무엇이든 담을 수 있는 제네릭 프로그래밍 -> ver 5.0<br>
*
* 사용하는 이유<br>
* 우리가 변수를 사용한다고 하면 항상 자료형을 먼저 지정하게 되어있다.<br>
* 변수의 이름이 같지만 데이터 타입(자료형)이 달라야 한다면<br>
* 제네릭 프로그래밍을 생각하자.
*/
public class GenericPrinter<T> {
// T 라는 대체 문자를 사용, E - element, K - key, V - value(사실은 아무 문자나 가능하다)
// 자료형 매개변수(type parameter)
// 이 클래스를 사용하는 시점에서 실제 사용될 자료형이 결정된다.
private T material; // T 대체 문자형으로 변수를 선언
public T getMaterial() {
return material;
}
public void setMaterial(T material) {
this.material = material;
}
// GenericPrinter<T> -- 참조 변수를 sysout(참조변수) --> 나의 멤버 material의 toString()으로 설계
@Override
public String toString() {
return material.toString();
}
}
package ch02;
import ch01.Plastic;
import ch01.Powder;
public class Maintest2 {
public static void main(String[] args) {
// 재료 선언
Plastic plastic01 = new Plastic();
Powder powder01 = new Powder();
Water water01 = new Water();
// 사용하는 시점에 T 대신 어떤 자료형을 사용할 지 지정 하면된다.
GenericPrinter<Plastic> genericPrinter1 = new GenericPrinter<>();
genericPrinter1.setMaterial(plastic01);
// 최상위 Object 를 활용 할때와 비교
// 형변환 할 필요가 없다 (다운 캐스트)
Plastic returnPlastic = genericPrinter1.getMaterial();
System.out.println(returnPlastic);
// 컴파일 시점에 오류를 알려줘서 안정적인 코드 작업이 진행된다.
// Powder returnPowder = genericPrinter1.getMaterial(); <-- 오류 발생
GenericPrinter<Water> genericPrinter2 = new GenericPrinter<>();
genericPrinter2.setMaterial(water01);
System.out.println(genericPrinter2);
// 제네릭 프로그래밍의 단점
// 사용하는 시점에 무엇이든 담을 수 있기 때문에 클래스 설계자 입장으로 바라볼때
// 의도하지 않은 타입이 들어올 수 있게 된다.
// 해결방법 <T extends 클래스> 문법을 사용한다.
} // end of main
}
4. 제네릭 <T extends 클래스> 활용
// 직접 객체를 사용할 수 없게 강제성을 부여 추상 클래스
public abstract class Material {
public abstract void doPrinting();
}
/**
* T extends 클래스 문법을 사용하기 위해 설계
*/
public class Plastic extends Material {
@Override
public String toString() {
return "재료는 플라스틱 입니다.";
}
@Override
public void doPrinting() {
System.out.println("플라스틱 재료로 출력합니다.");
}
}
/**
* T extends 클래스 문법을 사용하기 위해 설계
*/
public class Powder extends Material{
@Override
public String toString() {
return "재료는 파우더 입니다.";
}
@Override
public void doPrinting() {
System.out.println("파우더 재료로 출력합니다.");
}
}
/**
* @param <T> Material <br>
* Material을 상속받은 자식 클래스만 대체 문자에 들어올 수 있다.
*/
public class GenericPrinter<T extends Material> {
private T material;
public T getMaterial() {
return material;
}
public void setMaterial(T material) {
this.material = material;
}
@Override
public String toString() {
return material.toString();
}
}
public class MainTest3 {
public static void main(String[] args) {
// <T extends 클래스> 사용하기
// 상위 클래스의 필요성
// T 자료형은 범위를 제한할 수 없음
// 위 문법을 사용해서 상위 클래스에 속한 자료형만 대체 문자 안에 들어올 수 있다.
// ch03 패키지 자료형 사용
GenericPrinter<Powder> genericPrinter1 = new GenericPrinter<>();
genericPrinter1.setMaterial(new Powder());
System.out.println(genericPrinter1);
///////////////////////////////////////////////////////////////
// 컴파일 시점에서 부터 오류 발생을 한다.
// GenericPrinter<Water> genericPrinter2 = new GenericPrinter<>();
}
}
'Java > Java 유용한 클래스' 카테고리의 다른 글
공공데이터포탈 사용해 보기 - 30 (0) | 2024.06.04 |
---|---|
소켓을 활용한 HTTP 통신이란 - 28 (0) | 2024.06.03 |
네트워크 프로토콜이란? - 26 (1) | 2024.05.24 |
1: N 소켓 양방향 통신 - 25 (0) | 2024.05.24 |
1:1 양방향 통신(채팅 기본 기능 구현) - 24 (0) | 2024.05.24 |