KIC/JAVA

day25_1 - JAVA (자바, Generic)

바차 2021. 7. 19. 14:24
반응형

[제네릭 타입(Generic)]

-> '컴파일 단계'에서 '잘못된 타입이 사용될 수 있는 문제'를 제거 가능

 

-> 컬렉션, 람다식(함수적 인터페이스), 스트림, NIO에서 널리 사용

 

-> 제네릭을 모르면 API 도큐먼트 해석이 어려우므로 학습이 필요하다.

 

-> 일반적인 코드를 작성하고, 이 코드를 다양한 타입의 객체에 대하여 재사용하는 프로그래밍 기법이다.

 

->  클래스에서 사용할 타입을 클래스 외부에서 설정하는 타입이다.

 

 

 

 

 

[제네릭을 사용하는 코드의 이점]

-> 컴파일 시 강한 타입 체크가 가능

 

-> 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지한다.

 

 

[ex]

package javaPro.java_generic;

class MyClass01{

    @Override
    public String toString() {
        return "MyClass01 문장 실행";
    }
}

class Pool01 {
    private Object object;

    public void set(Object object) {
        this.object = object;
    }

    public Object get(){
        return object;
    }
}

public class GenericEx01 {
    public static void main(String[] args) {
        Pool01 gen = new Pool01();
        gen.set("그랜저"); // Object <------- String type
        String name = (String)gen.get();    
        // get() 으로 받아오는 타입이 Object 이므로 name에 저장할때 (String)으로 형변환 필요

        System.out.println(name);

        gen.set(new MyClass01()); // Object <---------- MyClass type
        MyClass01 g = (MyClass01)gen.get(); 
        // get() 으로 받아오느 타입은 이역시 object 이므로 g에 저장할 때 (MyClass01)타입으로 형변한 필요


        //String g2 (String) gen.get();  //이 문장은 error

        System.out.println(g); // g에 저장된 MyClass01 메서드 실행

    }
}

 

 

 

 

[제네릭 타입]

-> 타입을 파라미터로 가지는 클래스와 인터페이스

 

=> 선언 시 클래스 또는 인터페이스 이름 뒤에 '<>' 부호를 붙인다.

 

-> '<>' 사이에는 타입 파라미터 위치

 

타입 파라미터

-> 일반적으로 대문자 알파벳 한 문자로 표현

 

-> 개발 코드에서는 타입 파라미터 자리에 구체적인 타입을 지정해야 한다.

 

 

 

 

[제네릭 타입을 사용하지 않은 경우]

-> Object 타입 사용 -> 빈번한 타입 변환 발생 -> 프로그램 성능 저하

 

 

 

-> 제네릭은 타입이 Box 오브젝트로 들어가기 전부터 미리 타입을 사용자가 지정해서 집어넣는 기능이다.

 

 

 

 

 

[제네릭 타입을 사용한 경우]

-> 클래스를 선언할 때 타입 파라미터를 사용

 

-> 컴파일 시 타입 파라미터가 구체적인 클래스로 변경된다.

 

package javaPro.java_generic;

/*
제네릭을 이용 
타입을 정하지 않고  class를 구현하는 방법이다 
class의 멤버, 메소드 파라메터, 리턴 타입, 인터페이스의 타입을 실행시에 적용 할 수 있다
실행 사용시 제네릭 생략가능함  

*/

class Pool02<T> {
    private T t;

    public void set(T t) {
        this.t = t;
    }

    public T get(){
        return t;
    }
}

public class GenericEx02 {
    public static void main(String[] args) {
        // 제네릭 클래스이기 때문에 <String>를 지정해줘야 error가 안난다.
        Pool02<String> p = new Pool02<String>();
        p.set("hello");
        System.out.println(p.get());

        String str = p.get(); //이전 GenericEx1 코드와 다르게 (String) 형변환이 필요 없다.
        System.out.println(str);
        
        Pool02<Integer> p2 = new Pool02<Integer>();
        p2.set(1);
        System.out.println(p2.get());
        int value = p2.get();

        System.out.println(value);

    }
}

 

 

 

 

 

 

 

[제네릭 타입은 두 개 이상의 타입 파라미터를 사용 가능]

[ex]

package javaPro.java_generic;

/*
제네릭 : <T, M> 은 모든 문자가 가능 하다. 주로 영문 대문자 한글자를 쓴다
 *제네릭 class 구현  
 */

class Product<T, M> {
    private T kind;
    private M model;

    public T getKind() {
        return this.kind;
    }

    public void setKind(T kind) {
        this.kind = kind;
    }

    public M getModel() {
        return this.model;
    }

    public void setModel(M model) {
        this.model = model;
    }

    public String toString() {
        return "Product [kind =" + kind + ", model = " + model + "]";
    }
}

class Car {
    public String toString() {
        return "Car";
    }
}

class Tv {
    public String toString() {
        return "Tv";
    }
}

public class GenericEx3 {
    public static void main(String[] args) {
        Product<Tv, String> product1 = new Product<Tv, String>();
        product1.setKind(new Tv()); // 메소드를 매개변수로 받아 저장
        product1.setModel("스마트Tv"); // String 을 매개변수로 받아 저장
        Tv tv = product1.getKind(); // 리턴 값이 tv 메서드
        String tvModel = product1.getModel(); // 리턴 값이 String
        System.out.println("Tv 메서드 결과값: " + tv + "\n" + "tvModel 변수 저장값: " + tvModel);
        System.out.println(product1);

        Product<Car, String> product2 = new Product<Car, String>();
        product2.setKind(new Car());
        product2.setModel("디젤");

        Car car = product2.getKind();
        String carModel = product2.getModel();
        System.out.println("Car 메서드 결과값: " + car + "\n" + "carModel 변수 저장값: " + carModel);
        System.out.println(product2);
    }
}

 

 

자바 7부터는 다이아몬드 연산자를 사용해 간단히 작성 가능

 

 

 

[제네릭 메소드]

-> 매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드

 

-> 제네릭 메소드 선언 방법

   -> 리턴 타입 앞에 '<>' 기호를 추가하고 타입 파라미터를 기술

   -> 타입 파라미터를 리턴 타입과 매개변수에 사용한다.

[ex]

package javaPro.java_generic;

/* 
 * 제네릭  Method구현 : class 이용 하지 않고 메소드만을 제네릭으로 구현한다 
 * 실행 사용시 제네릭 생략가능함 
 * 
 * 
 */

class Pool04<T> {
    private T t;

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}
// 다양 한 방식의 메소드들을 Generic으로 구현한 코드

class Util04 {
    public static <T> Pool04<T> staticMethod(T t) {
        Pool04<T> box = new Pool04<T>();
        box.set(t);
        return box;
    }

    public <T> Pool04<T> instanceMethod1(T t) {
        Pool04<T> box = new Pool04<T>();
        box.set(t);
        return box;
    }

    public <T> String instanceMethod2(T t) {
        return t.toString();
    }
}

class MyCar {
    public String toString() {
        return "MyCar";
    }
}

public class GenericEx4 {
    public static void main(String[] args) {
        Pool04<Integer> box1 = Util04.<Integer>staticMethod(100); // 제네릭 생략 가능
        int intValue = box1.get();
        System.out.println(intValue);

        Util04 u = new Util04(); // 제네릭을 생략했기 때문에 어떤 값이든 가능

        Pool04<String> box2 = u.<String>instanceMethod1("홍길동"); // 생략 가능
        String strValue = box2.get();
        System.out.println(strValue);
        System.out.println(u.instanceMethod2(100)); // 정수 문자 메소드 다 잘 들어간다.
        System.out.println(u.instanceMethod2("김자바"));
        System.out.println(u.instanceMethod2(new MyCar()));
    }
}

 

 

 

 

[제네릭 타입 제한하기]

package javaPro.java_generic;

class Pool05<T extends Number> { // Number를 상속받은 타입으로만 제한하겠다는 의미
    T v;
    Pool05(T n){
        v = n;
    }
    void set(T n) {
        v = n;
    }
    T get() {
        return v;
    }
}

class Util05 {
    public static <T extends Number> int compare(T t1, T t2) {
        double v1 = t1.doubleValue();
        // System.out.println(t1.getClass().getName());
        double v2 = t2.doubleValue();
        // System.out.println(t2.getClass().getName());
        return Double.compare(v1, v2); // compare: 앞이 크면 1 출력 , 뒤가 크면 -1 출력
    }

}

public class GenericEx5 {
    public static void main(String[] args) {
        // 제네릭 클래스는 <> 생략이 가능하다.

        //T는 Number를 상속받는 타입으로만 제한했기 때문에 String 타입은 error
        //String str = Util05.compare("a", "b");

        int result1 = Util05.<Integer>compare(10, 20);
        System.out.println(result1); //1 출력
        int result2 = Util05.compare(4.5, 3);
        System.out.println(result2); //-1 출력

        Pool05<Integer> p1 = new Pool05<Integer>(20);
        Pool05<Double> p2 = new Pool05<Double>(20.0);

        //제네릭 에서 T는 Number를 상속받는 타입으로만 제한했기 때문에 String 타입과 Character은 error
        //Pool05<String> p3 = new Pool05<String>("hello"); 
        //Pool05<Character> p4 = new Pool05<Character>('a');
        
        Pool05<Byte> p5 = new Pool05<Byte>((byte) 1);
        int value = p1.get();
        System.out.println(value);
        System.out.println(p2.get());
        System.out.println(p5.get());
    }
}

 

 

 

[제네릭 타입 배열 입력]

package javaPro.java_generic;

//제네릭 타입 배열 입력하기 구현

class Generic1<T> {
    T[] v;

    public T[] getV() {
        return this.v;
    }

    public void setV(T[] v) {
        this.v = v;
    }

    public void print() {
        for (T s : v)
            System.out.print(s + ",");
        System.out.println();
    }
}

public class GenericEx7 {
    public static void main(String[] args) {
        Generic1<String> t = new Generic1<String>();
        String[] ss = { " 월매 ", " 순향 ", " 향단 " };
        t.setV(ss);
        t.print();
        for (String s : t.getV())
            System.out.println(s.trim());
        Integer[] ii = { 10, 20, 30 };
        // t.set(ii); // 지금 t는 String 타입이기 때문에 Integer 넣으면 오류

        Generic1<Integer> t2 = new Generic1<Integer>();
        t2.setV(ii);
        t2.print();
        for (Integer s : t2.getV())
            System.out.println(s / 10);
    
        }
}
300x250