JAVA/JAVA 정리

[JAVA] 제네릭

h0-0cat 2023. 5. 30. 11:52
728x90

 

제네릭

컴파일 시 강한 타입 체크를 할 수 있다.

타입 변환(casting)을 제거한다

 

 

비제네릭 코드

List list = new ArrayList();
list.add("hello");
String str = (String)list.get(0); //타입 변환을 해야한다.
 

 

제네릭 코드

List<String> list = new ArrayList<String>();
list.add("hello");
String str = list.get(0); // 타입 변환을 하지 않는다.
 

.

 

제네릭 타입(class<T>,interface<T>)

  • 제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다.
  • 제네릭 타입은 클래스 또는 인터페이스 이름 뒤에 “<>”부호가 붙고, 사이에 타입 파라미터가 위치한다.
public class 클래스명<T> {...}
public interface 인터페이스명<T> {...}
 
Object object = 자바의 모든 객체;
 

 

 

 

멀티 타입 파라미터(Class<K,V, ...> , Interface<K,V, ...>)

제네릭 타입은 두개 이상의 멀티 타입 파라미터를 사용할 수 있다.

각 타입 파라미터는 콤마로 구분한다.

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

    public T getKind(){return kind;}
    public M getModel(){return model;}

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

}
 

제네릭 객체 생성

public class ProductExample {
    public static void main(String[] args) {
        Product <Tv,String > product1 = new Product<>();
        // <>를 다이아몬드 연산자라 부른다.
        product1.setKind(new Tv());
        product1.setModel("스마트 tv");

        Product<Car,String> product2 = new Product<>();
        product2.setKind(new Car());
        product2.setModel("스마트 Car");

        System.out.println(product1.getKind());
        System.out.println(product2.getKind());
        System.out.println(product1.getModel());
        System.out.println(product2.getModel());
    }
}
 

제네릭 메소드(<T,R> R method(T t))

  • 제네릭 메소드는 매개 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드를 말한다.
  • 제네릭 메소드를 선언하는 방법은 리턴 타입 앞에 <>기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로 타입 파라미터를 사용하면 된다
public <타입파라미터, ...> 리턴타입 메소드명(매개변수,...) {...}
public <T> Box<T> boxing(T t) {...}
 
리턴타입 변수 = <구체적타입> 메소드명(매개값); //명시적으로 구체적 타입을 지정
리턴타입 변수 = 메소드명(매개값);  // 매개값을 보고 구체적 타입을 추정 
 
Box<Integer> box = <Integer>boxing(100);   // 타입 파라미터를 명시적으로 Intrger로 지정
Box<Integer> box = boxing(100); // 타입 파라미터를 Intrger로 추정 
 

 

 

제한된 타입 파라미터(<T extends 최상위타입>)

제한된 타입 파라미터를 선언하려면 타입 파라미터 뒤에 extends 키워드를 붙이고 상위 타입을 명시하면 된다.

상위타입은 클래스뿐만 아니라 인터페이스도 가능하다.(인터페이스라고 implements를 사용하지 않고 똑같이 extends를 사용한다.)

주의할 점은 메소드의 중괄호 {} 안에서 타입 파라미터 변수로 사용 가능한것은 상위 타입의 멤버(필드, 메소드)로 제한한다. ( 하위 타입이면 다른 하위 타입이 치환되지않아 컴파일에러가 날 수 있으므로)

public <T extends 상위타입> 리턴타입 메소드명(매개변수,...) {...}
 
static  <T extends Number> int numberCompare(T t1, T t2){
        Double a = t1.doubleValue();   // Number의 doubleValue() 메소드 사용
        Double b = t2.doubleValue();   // Number의 doubleValue() 메소드 사용

        return Double.compare(a,b);
    }
 

 

와일드카드 타입(<?>,<? extends…>, <? super …>)

=> 코드에서 ?를 일반적으로 와일드카드(wildCard) 라고 부른다.

  • 제네릭타입 <?> : Unbounded Wildcards(제한 없음) -> 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.
  • 제네릭타입<? extends 상위 타입> : Upper Bounded Wildcards(상위 클래스 제한) (내림차순) -> 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만이 올 수 있다.
  • 제네릭타입<? super 하위 타입> : Lower Bounded Wildcards (하위 클래스 제한) (오름차순) -> 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입만이 올 수 있다.

Course<?> => 수강생은 모든 타입(Person, Worker, Student, HightStudent)이 될 수 있다.

Course<? extends Student> -> 내림차순 => 수강생은 Student 와 HighStudent만 될 수 있다.

Course<? super Worker> -> 오름차순 => 수강생은 Worker 와 Person 만 될 수 있다.

 

제네릭 타입의 상속과 구현

 

  • 제네릭 타입도 다른 타입과 마찬가지로 부모 클래스가 될 수 있다. 다음은 Product <T,M> 제네릭 타입을 상속해서 ChildProduct<T,M> 타입을 상속한다.

 

public class ChildProduct<T,M> extends Product<T,M> {...}
 
public class ChildProduct<T,M,C> extends Product<T,M> {...}
 

 



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

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

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

 

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

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

 

JDK 1.5 이전에서는 여러 타입을 사용하는 대부분의 클래스나 메소드에서 인수나 반환값으로 Object 타입을 사용했습니다.

지만 이 경우에는 반환된 Object 객체를 다시 원하는 타입으로 타입 변환해야 하며, 이때 오류가 발생할 가능성도 존재합니다.

하지만 JDK 1.5부터 도입된 제네릭을 사용하면 컴파일 시에 미리 타입이 정해지므로, 타입 검사나 타입 변환과 같은 번거로운 작업을 생략할 수 있게 됩니다.

 

제네릭의 선언 및 생성

자바에서 제네릭은 클래스와 메소드에만 다음과 같은 방법으로 선언할 수 있습니다.

예제

class MyArray<T> {

    T element;

    void setElement(T element) { this.element = element; }

    T getElement() { return element; }

}
 

위의 예제에서 사용된 'T'를 타입 변수(type variable)라고 하며, 임의의 참조형 타입을 의미합니다.

꼭 'T'뿐만 아니라 어떠한 문자를 사용해도 상관없으며, 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있습니다.

타입 변수는 클래스에서뿐만 아니라 메소드의 매개변수나 반환값으로도 사용할 수 있습니다.

 

위와 같이 선언된 제네릭 클래스(generic class)를 생성할 때에는 타입 변수 자리에 사용할 실제 타입을 명시해야 합니다.

예제

MyArray<Integer> myArr = new MyArray<Integer>();
 

또한, Java SE 7부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있습니다.

MyArray<Integer> myArr = new MyArray<>(); // Java SE 7부터 가능함.
 

728x90