본문 바로가기
Work/Java

[Java] Generics 제네릭 - 총정리

1. 제네릭 개념

 

자바에서 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다.

제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.

이렇게 컴파일 시에 미리 타입 검사(type check)를 수행하면 다음과 같은 장점을 가진다.

 

1. 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있습니다

2. 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있습니다.

 

 

제네릭스(Generics)는 자바 Jaba 1.5 이후에 도입된 새로운 개념이다.

재너릭은 객체생성시 타입에 대한 정의를 생략하던 이전과 달리,

임시 자료형을 통해 구현시 타입에 자유도를 올리고, 객체로 사용시 엄격한 타입명시를 통해,

객체 지향적 코딩을하며, 형변환에 의한 불필요한 코딩을 줄이고, 잘못된 형변환에 의한 런타임 오류에서 벗어날 수 있게 된다.

 

// 재너릭 미사용
ArrayList aList = new ArrayList();

aList.add("hello");

String hello = (String) aList.get(0);



// 재너릭 사용문
ArrayList<String> aList = new ArrayList<>();

aList.add("hello");

String hello = aList.get(0);

 

과거 Object 타입으로 선언된 ArrayList를 사용하고 가져올때는, Object를 상속한 아무 자료형(Int, String, char, bool ...) 이 전부다 들어가버렸고,

가지고 올때는 형변환을 통해서 가져와야 사용이 가능했다.

이러한 이유로 런타임에러와 잦은 형변환, 예측할 수 없는 데이터 자료형까지 Object타입안에 들어가버려서 골머리를 앓았다.

 

하지만, 1.5이후 제너릭이 도입되고 나서는 가상의 데이터타입을 지정해주게 되었고,

Object타입이 아닌 자신이 만드는 임의에 타입으로 클래스를 선언하고,

인스턴스를 생성하는 부분에서 자료형을 지정해 사용해주는 방법이 도입되었다.

 

 

 

2. 제네릭 이해를 위한 예제 

 

재네릭 미사용한 Box Class 예제 

// Box Class
	public class Box {
        private Object obj;
        public void setObj(Object obj){
        this.obj = obj;
        }

        public Object getObj(){
        return obj;
        }
    }

// 사용시 Object타입과 그를 상속한 모든 타입이 들어감 (제한적이면서도, 사용에 유의했어야함)
        public class BoxExam {
        public static void main(String[] args) {
            Box box = new Box();
            box.setObj(new Object());				// 꺼낼때 주의가 필요
            Object obj = box.getObj();

            box.setObj("hello");
            String str = (String)box.getObj();		// 꺼낼때 주의가 필요
            System.out.println(str);

            box.setObj(1);
            int value = (int)box.getObj();			// 꺼낼때 주의가 필요
            System.out.println(value);
        }
    }

클래스 선언시 Object 타입으로 선언하는것이 가장 다형성을 가지지만, 결국 Object와 그를 상속한 타입으로만 사용가능하다.

사용시에는 구체적인 타입을 설정하지 않기 때문에, 다양한 타입의 데이터가 들어가게 되고,  

이로 인해 대부분의 자료형 사용시 , 위와같이 형변환이 필수적이였습니다.

 

 

제네릭을 사용한 Box Class 예제

// 임의의 임시 자료형 E를 명시
	public class Box<E> {
        private E obj;				// E 타입 변수 선언
        public void setObj(E obj){		// E 타입 변수를 받아 지정
            this.obj = obj;
        }

        public E getObj(){			// E 타입 변수를 반환
            return obj;
        }
    }
    

	public class BoxExam {
        public static void main(String[] args) {
            Box<Object> box = new Box<>();		// 객체 생성시 하나의 타입을 지정하여 생성
            box.setObj(new Object());
            Object obj = box.getObj();			// 예측 가능한 자료형

            Box<String> box2 = new Box<>();
            box2.setObj("hello");
            String str = box2.getObj();			// 예측 가능한 자료형
            System.out.println(str);

            Box<Integer> box3 = new Box<>();
            box3.setObj(1);
            int value = box3.getObj();			// 예측 가능한 자료형
            System.out.println(value);
        }
    }

generic을 사용함으로써 선언할때는 가상의 타입으로 선언하고,

사용시에는 구체적인 타입을 설정함으로써

다양한 타입의 클래스를 이용하는 클래스를 만들 수 있습니다.

 

 

3. 제네릭의 표현

제네릭의 타입이름에는 제한이 없다. 

그래서 일반적으로 임의이 자료형을 명시할 때, 아래와 같이 사용이 암묵적으로 약속 되어 있다.

아래와 같이 반드시 지킬 필요는 없으나, 일반적인 사용에 대한 약속이니, 모두가 읽기 쉬운 코드를 위해서 알맞게 사용하자. 

타입 설명
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number

 

 

4. 제네릭의 제한

제네릭에서 타입에 제한을 두는 방법이 있다.

와일드 카드 ? 를 사용하여 아래와 같이 세가지의 경우로 나눌수 있다.

 

<? extends T> : 상한 경계 : T 클래스를 상속하는 자식클래스들만 올 수 있다.

<? super T> : 하한 경계 : T 클래스의 부모 클래스만 올 수 있다. (더 제한적임)

<?> = <? extends Object> : 기존 경계 : 오브젝트 타입을 상속하는 모든 타입이 들어올 수 있다.

 

세번째는 결국 제네릭을 사용하지 않던, 과거 대다수 자료형들의 선언문과 같다고 할 수 있다.

다양한 자료형을 받아드리는 대신, 사용은 그 만큼 까다로울것이다.

 

 

 

 

차암조

https://wikidocs.net/268

https://st-lab.tistory.com/153

https://programmers.co.kr/learn/courses/9/lessons/257

'Work > Java' 카테고리의 다른 글

[Java] Annotation 어노테이션 - 총정리  (0) 2021.06.30
[Java] Java에 대한 상식  (0) 2021.06.30
[Java] 자바 공부 사이트 모음  (0) 2021.06.29
[Java] System 클래스 -정리  (0) 2021.06.29
[Java] Java Collection 구조 정리  (3) 2021.06.28