[Java] 5. 제네릭-개념


제네릭(Generic) 개념에 대한 설명 포스팅이다.



1. 제네릭(Generic)

  • JDK 1.5 이후
  • 자바에서 제네릭(generic)이란 데이터의 타입(data type)을 일반화하는(generalize) 것을 의미
  • 타입을 미리 정의하면 컴파일 시에 타입을 체크(type check)해 주는 기능
  • 런타임(실행 시)에 발생하는 에러를 예방하기 위해서 먼저 컴파일때 타입 체크를 하는 것.
  • 객체의 타입 지정함으로서 이외의 타입을 제한하기에 프로그래머의 형변환 실수를 컴파일 단계에서 줄일 수 있다.
  • 어떤 타입을 가지는지 개발자가 알 수가 있다. 타입 추론(Type Inference)
  • 자바 코드에서 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입 변환.
  • 컴파일 시에는 자바 코드 내의 모든 제네릭 타입은 제거가 된 상태로 class파일이 된다. jdk 1.5 버전 이전 코드와의 호환성(안정성)을 유지하기 위해서이다.


레거시 시스템이 아니면 제네릭을 다들 사용하고 있을 것이다. < >가 바로 제네릭 표현식이다. 아래와 같은 코드를 많이 봤을 것이다.

HashMap<String, Notice> noticeMap = new HashMap<>();



1-2. 타입 체크(Type Check)

타입을 정의하면 프로그래머가 형변환 실수을 줄 일 수 있다.

//JDK 1.5 이전 코드 
ArrayList boardList1 = new ArrayList();
boardList1.add(new Notice());

Notice notice1 = (Notice) boardList1.get(0);//형변환
//제네릭을 타입 정의한 코드
ArrayList<Notice> boardList2 = new ArrayList<Notice>();
boardList2.add(new Notice());
//boardList2.add(new FAQ());//타입 체크 에러 발생!

Notice notice2 = boardList2.get(0);



1-2. 타입 추론(Type Inference)

프로그래머가 변수가 어떤 데이터 타입을 가지는지 알수 가 있다.

ArrayList<Notice> boardList1 = new ArrayList<Notice>();
ArrayList<Faq> boardList2 = new ArrayList<Faq>();



1-3. 제네릭 명칭 설명

class Box<T> {}
  • Box : 제네릭 클래스, T의 Box
  • T : 타입변수, 타입 매개변수(T는 타입문자)
  • Box : 원시 타입(raw type)



1-4. 타입 매개변수명

제네릭에서 참조형 타입(reference type) 간단히 말해서 타입(type)을 의미하는 'T'
요소(Element)를 의미하는 'E'이고 아래는 가장 일반적으로 쓰이는 타입 매개변수명이다.


가장 일반적으로 쓰이는 타입 매개변수명

타입 매개변수명의미
T타입(Type)
E요소(Element)
K키(Key)
N숫자(Number)
V값(Value)
S, U, V 등2번째, 3번째, 4번째 타입



2. 제네릭 클래스

우선 예제 코드는 강사는 강의 정보가 필요하고
학생은 강의와 강사 정보가 필요한 개념이다.


강의, 강사, 학생 클래스

//강의 클래스
class Lecture {
    private String name;    //강의명
    private String day;     //강의요일

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDay() {
        return day;
    }

    public void setDay(String day) {
        day = day;
    }
}

//강사 클래스
class Teacher<T1> {
    private T1 t1;//lecture

    public T1 getT1() {
        return t1;
    }

    public void setT1(T1 t1) {
        this.t1 = t1;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "t1=" + t1 +
                '}';
    }
}

//학생 클래스
class Student<T1, T2> {
    private T1 t1;//lecture

    private T2 t2;//teacher

    public T1 getT1() {
        return t1;
    }

    public void setT1(T1 t1) {
        this.t1 = t1;
    }

    public T2 getT2() {
        return t2;
    }

    public void setT2(T2 t2) {
        this.t2 = t2;
    }
}

main 메서드

//예제 1. 제네렉 T(type) 사용 예제
//예제 1-1. 제네릭 'T'에 Lecture 대입 ( Lecture의 Teacher )
Teacher<Lecture> lectureTeacher = new Teacher<Lecture>();

Lecture lecture = new Lecture();
lecture.setName("자바");
lecture.setDay("수요일");
lectureTeacher.setT1(lecture);

System.out.println(lectureTeacher.toString());

//예제 1-2. 제네릭 'T'에 String(Wrapper Class) 대입  ( String의 Teacher )
Teacher<String> lectureTeacher2 = new Teacher<String>();
lectureTeacher2.setT1("자바:수요일");

System.out.println(lectureTeacher2.toString());



예제 코드를 보면 Teacher 제네릭 클래스는 모든 클래스가 타입변수로 정의가 된다.
아직 제한하지 않은 제네릭 클래스이기 떄문이다.

class Teacher<T1> {
    private T1 t1;//lecture
    //...
}


그래서 객체 생성 시에 타입변수를 “Lecture” 또는 “String” 클래스를 정의에 따라
t1 인스턴스 변수에 들어가는 데이터가 다르다.

Teacher<Lecture> lectureTeacher = new Teacher<Lecture>();
Teacher<String> lectureTeacher2 = new Teacher<String>();


출력 값을 확인해보면 아래와 같다.

Teacher{t1=com.example.generic.sample.Lecture@71be98f5}//객체 형태
Teacher{t1=자바:수요일}//String 형태



3. 멀티제네릭 클래스

아래는 멀티제네릭 클래스 예제이다. 제네릭 클래스에 타입변수를 여러개 정의한 것이다.
아래 주석 셜멍과 같이 해당 프로그램에서는 학생(Stduent)은 강사정보와 강의정보 데이터가 필요한데 제네릭에 제한하지 않으면 프로그래머의 실수로 논리적 오류가 발생할 수 있다.

다음 포스팅에서 제한 된 제네릭 클래스에 대해서 설명 할 예정이다.


//예제 2. 멀티제네릭 예제(타입변수를 여러개 정의한 제네릭클래스)
//예제 2-1. 정상적인(원하느) 프로그렘
Student<Lecture, Teacher> student = new Student<Lecture, Teacher>();
student.setT1(new Lecture());//강사 정보
student.setT2(new Teacher());//강의 정보

//예제 2-2. 논리적 오류 프로그램
//Student 제네릭 클래스가 강의와 강사 클래스로만 객체가 생성되어야 하는데
//아무 타입변수로 객체로 생성되기에 프로그래머가 임믜로 잘못된 방식으로 개발 할 수 았음
Student<String, Integer> student2 = new Student<String, Integer>();
student2.setT1("아무데이터");
student2.setT2(123456789);



해당 포스팅은 여기서 종료…