본문 바로가기

카테고리 없음

[Easy Java] 05.Generic

무단 도용을 금합니다. (꼭 출저를 써주시길 부탁드립니다.)

※ 이 글은 프로그래밍을 시작한지 얼마 안된 분에게는 다소 내용이 어려울 수 있습니다.

개발 환경

  • Java : jdk-1.7.0-xx
  • 툴 : Eclipse kepler EE
  • DBMS : MySQL Community Server 5.6


1. 제너릭이란?

멤버 필드 그리고 메서드와 파라메터의 타입을 정의하지 않고 메서드를 구현할 때 타입을 나타내는 기법입니다.
정해지지 않은 타입이 정해지는 시점은 컴파일시 정해집니다.


//GenericTest1.java (한글명: 노가다.자바)
package test;

public class GenericTest1 {
    public int equal(int a, int b){
        if(a == b) { return a; }            //정수형 a와 정수형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }
    public double equal(double a, double b){
        if(a == b) { return a; }            //더블형 a와 더블형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }
    public byte equal(byte a, byte b){
        if(a == b) { return a; }            //바이트형 a와 바이트형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }
    public long equal(long a, long b){
        if(a == b) { return a; }            //롱형 a와 롱형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }
    public float equal(float a, float b){
        if(a == b) { return a; }            //플로트형 a와 플로트형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }

    public char equal(char a, char b){
        if(a == b) { return a; }            //문자형 a와 문자형 b를 비교하여 둘중에 큰 값을 리턴
        else { return b;}
    }
}


//TestMain.java
package test;

public class TestMain {
    public static void main(String[] args){
        GenericTest1 gt1 = new GenericcTest1();
        
        int resultInteger = gt1.equal( 1245 , 2348 );    //정수형
        double resultDouble = gt1.equal( 7.5 , 3.6);    //더블형
        byte resultByte = gt1.equal( 1 , 2 );        //바이트형
        long resultLong = gt1.equal( 10000000000L , 20000000000L );        //롱
        float resultFloat = gt1.equal( 3.1f , 5.4f );        //플로트형
        char resultChar = gt1.equal( 'c' , 'z' );        //문자
        
        System.out.println( "정수형 결과 : "+resultInt );
        System.out.println( "더블형 결과 : "+resultDouble );
        System.out.println( "바이트형 결과 : "+resultByte );
        System.out.println( "롱형 결과 : "+resultLong );
        System.out.println( "플로트형 결과 : "+resultFloat );
        System.out.println( "문자형 결과 : "+resultChar );
    }
}



이 소스에서 equal라는 메서드를 오버로딩(Overloading)하여 여러 형식을 비교할수 있도록 구현을 했습니다.
하지만 이 많은 기본자료형들을 일일이 하나하나 선언해주는게 쉬울까요?

만약 메서드가 equal가 아닌 다른 여러 메서드를 자료형의 수 만큼 만들어줘야 한다면
이방법은 별것 아닌 작업임에도 굉장히 번거롭고 시간도 들겠지요.
제일 중요한 단점같은 기능의 메서드를 필요이상으로 오버로딩을 많이 하게되는 것입니다.

이것을 해결하기 위해 나온것이 제네릭(Generic)입니다.


제네릭은 자료형을 암묵적으로 선언합니다.
제네릭의 형식을 보면 이해가 되실겁니다.

위의 소스에서 정의했던 equal를 오버로딩하지 않고 하나로 만들 수 있는 방법이죠.

//GenericTest2.java
package test;

public class GenericTest2<T> {
    public equal(a, b){
        if(a > b) { return a; }
        else { return b;}
    }
}

아까 위에 구구절절 쓴 그 길었던 클래스가 이렇게 변했습니다!

사용하는 방법을 볼까요?

//TestMain.java
package test;

public class TestMain {
    public static void main(String[] args){
        GenericTest2<Integergt2 = new GenericcTest2<Integer>();
        
        int resultInteger gt2.equal( 1245 , 2348 );    //정수형
        
        System.out.println( "정수형 결과 : "+resultInteger );

    }
}

제너릭을 사용하면 이렇게 간편해집니다.
C++언어의 template 기능과 아주 유사하면서 정의하고 사용하는 부분은 더 심플한것 같군요.

제너릭 클래스 정의부분을 보면 클래스의 이름 뒤에 <T>라는 것이 붙었는데 이것을 저는 제너릭 타입이라고 하겠습니다.

제너릭 타입은 기본 자료형은 쓸 수 없고 클래스형식의 Wrapper클래스를 이용하여 사용할 수 있습니다.
물론 Wrapper클래스가 아닌 어떤 클래스도 올 수 있습니다.

제너릭 타입은  타입 파라메터를 선언하여 사용하는데 타입 파라메터는 <와 >사이에 들어가는 매개변수인데
위의 예제에서는 T라는 타입 파라메터를 사용한 것입니다. 또한 각자 다른 이름으로 ,를 이용해 여러개의 타입 파라메터를 선언할 수도 있습니다.

타입 파라메터는 다른 변수나 객체들과 혼동하지 않기위해 보통 대문자 알파벳으로 사용하는데 일반적으로 사용하는 관습이 있습니다.

  • E : 요소(Element)의 약자입니다. 보통 컬렉션의 타입을 정할때 사용합니다.
  • K : 키(Key)를 나타냅니다.
  • V : 값(Value)를 나타냅니다.
  • T : 타입(Type)을 나타냅니다.

제너릭 타입은 저 위의 예제처럼 클래스의 이름 뒤에 제너릭 타입을 선언해서 사용할 수 있습니다.


2.제너릭 메서드

또 다른 사용 방법으로는 제너릭 메소드를 정의하는 것인데요.
방법은 위에서 보신것과 크게 다르지 않습니다.

//GenericTest3.java
package test;

public class GenericTest3 {
    public <T>equal(a, b){
        if(a == b) { return a; }
        else { return b;}
    }
}

이런 식으로 사용하게되면 메서드 하나에서만 제너릭을 사용할 수 있습니다.


실제로 제너릭을 자주 사용하는 것은 컬렉션입니다.
이 내용에 대해서는 컬렉션에서 알아보도록 하겠습니다.


제너릭을 사용하게 되면 다음과 같은 장점이 있습니다.

  • 동적으로 타입이 결정되지 않아 컴파일 전에 오류를 수정할 수 있다.
    때문에 제너릭이 처리되는 코드는 런타임(실행중)에러를 내지 않습니다.
  • 자바에서 번거로운 캐스팅절차를 거치지 않습니다.
    때문에 캐스팅에 의한 예외 상황을 줄일수 있습니다.


이상으로 제너릭에 대한 설명을 마치겠습니다.