제네릭

JUNG GYUWON·2023년 10월 13일
0

Java

목록 보기
9/18

제네릭

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

일반화를 뜻하는 generalize라는 단어에서 유래한 것으로 다양한 타입을 사용하는 클래스나 메소드에서 일반화되어 코드를 작성하고 사용할 때 데이터 타입을 지정하는 방식

왜 필요한가?
JDK 1.5 이전에는 여러 타입을 사용하는 클래스나 메소드에서는 Object 타입을 사용했다

근데 이러면 Object 객체를 다시 원하는 타입으로 변환해야 하고 이 과정에서 오류가 발생할 가능성도 있다.

제네릭을 사용하면 컴파일 시에 미리 타입이 정해서, 타입 검사나 타입 변환과 같은 번거로운 작업을 생략할 수 있게 된다

장점:

  • 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다
  • 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다

제네릭 사용법

class MyArray<T> { // T는 타입변수이며 다른 이름을 사용해도 된다, 여러개도 가능
    T element;

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

    T getElement() { return element; }
}

MyArray<Integer> myArr = new MyArray<Integer>(); // 다음과 같이 생성

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

+) 타입변수 자리에 실제 타입을 명시할 때는 기본 타입이 아닌 Wrapper 클래스를 명시한다

import java.util.*;

class LandAnimal { public void crying() { System.out.println("육지동물"); } }

class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹"); } }

class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }

class Sparrow { public void crying() { System.out.println("짹짹"); } }

class AnimalList<T> {
    ArrayList<T> al = new ArrayList<T>();

    void add(T animal) { al.add(animal); }

    T get(int index) { return al.get(index); }

    boolean remove(T animal) { return al.remove(animal); }

    int size() { return al.size(); }
}

 

public class Generic01 {

    public static void main(String[] args) {

        AnimalList<LandAnimal> landAnimal = new AnimalList<>(); // Java SE 7부터 생략가능함.

        landAnimal.add(new LandAnimal());

        landAnimal.add(new Cat());

        landAnimal.add(new Dog());

        // landAnimal.add(new Sparrow()); // 오류가 발생함. 

        for (int i = 0; i < landAnimal.size() ; i++) {
            landAnimal.get(i).crying();
        }
    }
}

위의 예제에서 Cat과 Dog 클래스는 LandAnimal 클래스를 상속받는 자식 클래스이므로, 부모 클래스로 타입변환이 되기 때문에 AnimalList<LandAnimal>에 추가할 수 있다.


제네릭의 제거 시기

제네릭은 컴파일 시 컴파일러에 의해 코드에 선언된 타입으로 변환되어 class 파일에는 제네릭 타입이 아닌 선언한 타입의 클래스나 메소드만 포함된다

제네릭이 없는 코드와의 호환성을 위해서 이를 제거한다

즉 사용자의 편의를 위한 기능이지 컴퓨터는 제네릭 사용 여부에 관계없이 똑같이 작동함


타입 변수의 제한

타입 변수에 extends 키워드를 사용하여 특정 타입만을 사용하도록 제한할 수 있다

class AnimalList<T extends LandAnimal> { ... }

위와 같이 클래스가 아닌 인터페이스를 구현할 경우에도 implements 키워드가 아닌 extends 키워드를 사용해야한다
interface WarmBlood { ... }

class AnimalList<T extends WarmBlood> { ... }

클래스와 인터페이스를 동시에 상속받고 구현해야 한다면 & 기호를 사용하면 된다
class AnimalList<T extends LandAnimal & WarmBlood> { ... }

제네릭 메소드

메소드의 선언부에 타입 변수를 사용한 메소드

앞서서는 클래스에서 제네릭을 선언하는 것을 했고 지금은 메소드 단에서 선언하는 것

public static <T> void sort( ... ) { ... }
// 타입변수는 메소드 선언부에서 반환타입 앞에 위치함

와일드 카드

주로 메소드의 매개변수의 다양한 타입을 지정할 때 사용

<?>           // 타입 변수에 모든 타입을 사용할 수 있음.
<? extends T> // T 타입과 T 타입을 상속받는 자손 클래스 타입만을 사용할 수 있음.
<? super T>   // T 타입과 T 타입이 상속받은 조상 클래스 타입만을 사용할 수 있음.

그러면 와일드 카드랑 제네릭이랑은 뭐가 다른거지?

제네릭:

  • 클래스나 메소드에 주로 사용된다
  • 다양한 타입을 지정한 T를 사용하여 해당 타입을 반복적으로 참조할 때 사용한

    와일드 카드:
  • 주로 메소드의 매개변수 타입으로 사용됩니다.
  • 정확한 타입 정보가 필요하지 않거나, 메서드가 여러 다양한 타입의 인자로 호출될 수 있게 하기 위해 사용하는 용도이다

결국 모든 타입을 받기는 하지만 결국에 사용자가 지정한 한가지 타입을 반복적으로 사용할 때는 제네릭을 써야하고 단순히 다양한 타입의 매개변수를 받을 수 있는 것을 지정할때는 와일드 카드를 쓰면 된다

import java.util.*;

class LandAnimal { public void crying() { System.out.println("육지동물"); } }

class LandAnimal { public void crying() { System.out.println("육지동물"); } }

class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹"); } }

class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }

class Sparrow { public void crying() { System.out.println("짹짹"); } }

class AnimalList<T> {
    ArrayList<T> al = new ArrayList<T>();
    
    public static void cryingAnimalList(AnimalList<? extends LandAnimal> al) {
    
        LandAnimal la = al.get(0);

        la.crying();
    }

    void add(T animal) { al.add(animal); }

    T get(int index) { return al.get(index); }

    boolean remove(T animal) { return al.remove(animal); }

    int size() { return al.size(); }
}

 
public class Generic03 {
    public static void main(String[] args) {
        
        AnimalList<Cat> catList = new AnimalList<Cat>();

        catList.add(new Cat());

        AnimalList<Dog> dogList = new AnimalList<Dog>();

        dogList.add(new Dog());

        AnimalList.cryingAnimalList(catList);

        AnimalList.cryingAnimalList(dogList);
    }
}
profile
반가워요😎

0개의 댓글