[자바] Generic

Gammi·2022년 12월 13일
0

JAVA

목록 보기
32/35

📚 제네릭(Generic)


  • 일반화

  • 클래스 정의 시 사용되어질 데이터타입을 미리 명시하지 않고 객체 생성 시 데이터 타입을 명시하여 사용하는 기법

  • 주로 Collection API 클래스들에 제네릭이 적용되어 있으므로 인스턴스 생성 시 제네릭 타입으로 사용할 데이터 타입 지정

    -> 지정된 데이터 타입이 클래스 내 임시 데이터 타입 대체함


⭐ Ex

import java.util.*;

List<String> list = new ArrayList<String>();
// 컬렉션 요소로 사용되는 데이터가 String 타입으로 고정

Set<Integer> set = new HashSet<Integer>();
// 컬렉션 요소로 사용되는 데이터가 Integer 타입으로 고정

Map<Integer, String> map = new HashMap<Integer, String>();
// 컬렉션 요소 중 키는 Integer, 값은 String 타입으로 고정





📕 제네릭 클래스 정의


  • 클래스 정의 시점에서 클래스명 뒤에 <> 기호 사용하여 가상의 자료형 명시

    -> 보통 1글자 영어 대문자 사용(E 또는 T)

    -> int형 대신 Integer, char형 대신 Character

  • 가상의 자료형은 클래스 정의 시점에 정확한 자료형을 명시하지 않지만, 클래서 정의 시점에서 데이터타입 대신 사용 가능

  • 해당 클래스 인스턴스 생성 시점에 실제 자료형을 지정하면 가상의 자료형이 실제 자료형으로 대체됨

    -> 인스턴스 생성 시점에서 어떤 자료형으로도 변형 가능!


⭐ Ex

public static void main(String[] args) {
  GenericClass<Integer> gc = new GenericClass<Integer>();
  // T 타입이 Integer 타입으로 지정됨
  gc.setMember(1);
  // 정수형 데이터 전달 가능
  gc.setMember("홍길동");
  // String 타입 데이터 전달 불가능!
  // 정수 타입 외 모두 컴파일 에러 발생!!!
  
  int num = gc.getMember();
//===========================================
  
  Person p = new Person("홍길동", 20);
  // 같은 패키지에 Person 클래스 정의되어 있어야 함
  GenericClass<Person> gc2 = new GenericClass<Person>();
  gc2.setMember(p);
  gc2.setMember(new Person("이순신", 45));
  Person person = gc2.getMember();
  // person 출력할 경우
  // 이순신, 45 출력됨
}

class GenericClass<T> {
// 제네릭 타입으로 T 지정
  T member;
  // 멤버변수 member의 타입이 T로 지정됨
  // 실제 데이터 타입X
  
  public T getMember() {
    return member;
  }
  public void setMember(T member) {
    this.member = member;
  }
  // Getter/Setter 메서드에서 member를 다뤄야 하는 경우에도
  // 실제 데이터 타입 대신 가상의 T 타입 사용 가능!
}





⛔ 제네릭 타입 사용 시 주의사항


  1. static 멤버 내에서 제네릭 타입 파라미터 사용 불가
  • 제네릭 타입은 인스턴스 생성 시점에서 실제 데이터타입으로 변환됨

  • 하지만 static 멤버는 인스턴스 생성 시점보다 먼저 로딩되므로 데이터타입이 지정되지 않아서 사용이 불가능함!


class GenericClass<T> {
  private T data; // 가능
  private static T staticMember; // 불가능
  // static 멤버변수에 제네릭 타입 파라미터 사용 불가!
  public static void staticMethod(T data) {} // 불가능
  // static 메서드에 제네릭 타입 파라미터 사용 불가
}

  1. new 연산자 사용 시 제네릭 타입 파라미터 사용 불가
  • 컴파일 시점에서 생성자 타입 확인 불가능

class GenericClass<T> {
  T instance = new T(); // 불가능
  // 인스턴스 생성 시 제네릭 타입 파라미터로 생성자 호출 불가능!
}

  1. instanceof 연산자 사용 시 제네릭 타입 파라미터 사용 불가
  • 컴파일 시점에서 T의 데이터타입 확인 불가능하므로 true, false 판별, 형변환 수행 불가능

public void compare() {
  Object o = new Object();
  if(o instanceof T) { // 불가능
  // 컴파일 시점에서 T의 데이터타입 확인 불가능함!
  }
}





제네릭 타입의 상속과 구현


  • 부모 타입에 제네릭 타입이 지정되어 있을 경우 서브클래스에서 상속받을 때 부모의 파라미터를 서브클래스 타입 파라미터로 명시

  • 자신만의 제네릭 타입도 추가 가능


⭐ Ex

class Class1<P> {}
interface Interface1<Q> {}

class SubClass<P, Q, R> extends Class1<P> implements Interface1<Q> {
  // 부모 타입 제네틱 타입 먼저 명시 후
  // 본인만의 제네릭 타입 R 추가
  P var1; // 슈퍼클래스 Class1 타입 P
  Q var2; // 슈퍼클래스 Interface1 타입 Q
  R var3; // 자신의 타입 R
}





파라미터 타입 제한


  • 제네릭 타입에 사용 가능한 파라미터 제한할 수 있음

  • 제네릭 타입 파라미터 선언 시 Object 타입과 그 자식 타입 모두 사용 가능

  • 필요에 따라 파라미터 타입에 올 수 있는 데이터 타입 제한 가능


< 기본 문법 : 파라미터에 대한 서브클래스 타입으로 제한하는 경우 >

class 클래스명<타입파라미터 extends 클래스타입> {}
  • 타입 파라미터(ex. E 또는 T 등)는 extends 뒤 클래스타입이거나 하위 타입만 지정 가능

⭐ Ex

class GenericClass<E> {}
// 타입 파라미터 E는 어떤 타입으로도 변경 가능

class GenericClass2<E extends Number> {}
// Number 타입 또는 Number 클래스 하위 타입으로만 변환 가능
// Wrapper 클래스가 Number 클래스 하위 타입!





➕ 제네릭 실습


제네릭 타입을 사용해 Person 객체 저장

import java.util.*;
public static void main(String[] args) {
  Person p1 = new Person("홍길동", 20);
  Person p2 = new Person("강감찬", 44);
  List<Person> list = new ArrayList<Person>();
  list.add(p1);
  list.add(p2);
  list.add(new Person("이순신", 30));
  // 제네릭 타입으로 지정했기 때문에 객체는 무조건 Person 타입이어야 함!
//list.add("강동원"); - String 타입 저장 불가!
  for(int i = 0; i < list.size(); i++) {
    Person p = list.get(i);
    System.out.println(p.name + ", " + p.age);
  }
  for(Person p : list) {
    System.out.println(p.name + ", " + p.age);
    // 향상된 for문도 사용 가능
  }
}
profile
개발자가 되었어요⭐️

0개의 댓글