⭐제네릭 프로그래밍

Generic Programming

 

타입을 일반화하여
재사용 가능한 코드를 작성하도록 하는 기법이다.

 

👍특히 데이터 타입이 다르지만
변수명을 동일하게 사용해야 한다면
제네릭 프로그래밍을 활용할 수 있다.

 

🤔Why?
타입의 안정성
코드의 재사용성
유지보수성

 

😎How?
클래스, 데이터타입, 메서드 등에서
<T> 같은 형태의 대체 문자열을 선언하면 된다.

 

T(ype) E(lement), K(ey), V(alue).. 아무 문자나 넣어도 상관없다.
이를 자료형 매개변수 (Type parameter) 라고 한다.

제네릭을 쓴 클래스는
사용하는 시점에 실제 사용될 자료형이 결정된다.

 

💀제네릭 프로그래밍의 문제점

개발자가 의도하지 않게 엉뚱한 클래스까지 들어올 가능성이 있다.

 

✨T extends 클래스
T 자료형의 범위를 제한한다.

 

 

예제1.

GenericPrinter 클래스, 이와 상호작용해는 재료클래스인 Plastic, Powder, Water 를 만들었다.

원래는 재료를 바꿀 때마다 새로운 클래스를 생성하거나 코드를 수정해야 하는 번거로움이 있다.

 

하지만 제네릭 프로그래밍을 해주면
형변환이 필요없고 좀 더 편하게 쓸 수 있다.

또한 컴파일 시점에 오류를 알려줘
안정적 코드작업을 돕는다.

package _generic.ch02;
/**
 5.8
 제네릭 프로그래밍을 사용해본다.

 클래스, 데이터타입, 메서드 등에서
 <T> 대체 문자열을 선언할 수 있다.
 */
public class GenericPrinter<T> {
    /*
    T(ype) 라는 대체 문자를 사용한다.
    E(lement), K(ey), V(alue).. 아무 문자나 넣어도 상관없다.
    이를 자료형 매개변수 (Type parameter) 라고 한다.

    제네릭을 쓴 클래스는
    사용하는 시점에 실제 사용될 자료형이 결정된다.
    */
    T material; // T 대체 문자형으로 변수를 선언할 수 있다.

    // get,set
    public T getMaterial() {
        return material;
    }
    public void setMaterial(T material) {
        this.material = material;
    }
    //method
    @Override
    public String toString() {
        return material.toString();
    }
}//class

 

1-2. 재료클래스

package _generic.ch02;
public class Plastic {
    @Override
    public String toString() {
        return "재료는 플라스틱 입니다.";
    }
}//Plastic
package _generic.ch02;
public class Powder {
    @Override
    public String toString() {
        return "재료는 파우더 입니다.";
    }
}//Powder
package _generic.ch02;
public class Water {
    @Override
    public String toString() {
        return "재료는 물 입니다.";
    }
}//Water

 

1-3. 코드 테스트부

package _generic.ch02;
public class MainTest2 {

    public static void main(String[] args) {

        //재료 선언
        Plastic plastic1 = new Plastic();
        Powder powder1 = new Powder();

        //사용하는 시점에 T 대신 어떤 자료형을 사용할 지 지정해준다.
        GenericPrinter<Plastic> gPrinter1 = new GenericPrinter<>();
        gPrinter1.setMaterial(plastic1);
        System.out.println(gPrinter1.toString()); // "재료는 플라스틱 입니다."

        //재료꺼내기
        Plastic usePlastic = gPrinter1.getMaterial(); //형변환이 필요없다

        /*
        제네릭으로 만들면
        형변환이 필요없고
        좀 더 편하게 쓸 수 있다.

        또한 컴파일 시점에 오류를 알려줘
        안정적 코드작업을 돕는다.
         */
//        Powder usePowder = gPrinter1.getMaterial();

        Water water1 = new Water();
        GenericPrinter<Water> gPrinter2 = new GenericPrinter<>();
        gPrinter2.setMaterial(water1);
        System.out.println(gPrinter2.toString()); // "재료는 물 입니다."

    }//main
}//class

 

 

예제2.

의도치않게..

물 같은 의도하지 않은 재료가 들어오는 것을 막으려면

<T extends Object> 구문을 활용하면 된다.

 

여기서는 Material 을 상속받은 재료 클래스만 대체 문자열에 들어올 수 있도록 해봤다.

package _generic.ch03;
/**
 5.8
 제네릭 프로그래밍을 사용하자
 <T> 다음은 <T extends Object>

 Material 을 상속받은 자식 클래스만
 대체 문자열에 들어올 수 있다.
 */
public class GenericPrinter<T extends Material> {

    //member
    T material;

    //get set
    public T getMaterial() {
        return material;
    }

    public void setMaterial(T material) {
        this.material = material;
    }

    //method
    @Override
    public String toString() {
        return material.toString();
    }
}//class

 

2-1. 추상클래스 Material과 재료(자식)클래스들 설계

package _generic.ch03;
public abstract class Material {
    public abstract void doPrining(); //추상메서드
}//class
package _generic.ch03;
public class Plastic extends Material {
    @Override
    public String toString() {
        return "재료는 플라스틱 입니다.";
    }
    @Override
    public void doPrining() {
        System.out.println("플라스틱을 사용해서 프린팅..");
    }
}//Plastic
package _generic.ch03;
public class Powder extends Material {
    @Override
    public String toString() {
        return "재료는 파우더 입니다.";
    }
    @Override
    public void doPrining() {
        System.out.println("파우더를 사용해서 프린팅..");
    }
}//Powder

 

2-1-2.

상속 받지 못한 Water 클래스

package _generic.ch03;
public class Water {
    @Override
    public String toString() {
        return "재료는 물 입니다.";
    }
}

 

2-2. 코드 테스트

 

제네릭 프로그래밍을 하면 타입의 안정성을 확보하고
<T extends Object> 같은 기능을 통해 타입에 제한을 둘 수도 있다.

package _generic.ch03;
public class MainTest3 {
    public static void main(String[] args) {

        //재료 선언
        Powder powder1 = new Powder();
        Plastic plastic1 = new Plastic();
        Water water1 = new Water();

        GenericPrinter<Powder> gPinter1 = new GenericPrinter<>();
        gPinter1.setMaterial(powder1);
        /*
        제네릭 프로그래밍을 하면
        타입의 안정성과
        T extends Object
        타입에 제한을 둘 수 있다.
         */

        //Water를 사용해보자 - 컴파일 시점 오류 발생
//        GenericPrinter<Water> gPrinter2 = new GenericPrinter<Water>();

    }//main
}//class

 

 

예제3.

제네릭 클래스 설계하고 활용

package _generic.ch04;
/**
 5.8
 제네릭 클래스 Box 설계
 */
public class Box<T> {

    //변수
    T material;

    //생성자
    public Box(T material) {
        this.material = material;
    }

    //get set
    public T getMaterial() {
        return material;
    }

    public void setMaterial(T material) {
        this.material = material;
    }

    //출력
    @Override
    public String toString() {
        return material.toString();
    }
}

 

3-1.

코드실행부

package _generic.ch04;
/**
 5.8
 제네릭 클래스 Box 설계
 */
public class GenericBoxDemo {

    public static void main(String[] args) {

        //String 타입 Box 선언 및 사용
        Box<String> stringBox = new Box<>("문자열");
        System.out.println(stringBox.toString());

        stringBox.setMaterial("변경된 문자열");
        System.out.println(stringBox.toString());

        //Integer 타입 Box 선언 및 사용
        Box<Integer> integerBox = new Box<>(1234);
        System.out.println(integerBox.toString());

        integerBox.setMaterial(4321);
        System.out.println(integerBox.toString());

        //Double 타입 박스 선언 및 사용
        Box<Double> doubleBox = new Box<>(0.1234);
        System.out.println(doubleBox.getMaterial());

        doubleBox.setMaterial(0.4321);
        doubleBox.toString();

    }//main
}//class

+ Recent posts