[Java] 제네릭(generic)

JTI·2022년 11월 28일
0

☕️  Java

목록 보기
30/59
post-thumbnail

💡 제네릭이란?


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

JDK 1.5부터 도입한 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.
이렇게 컴파일 시에 미리 타입 검사(type check)를 수행하면 다음과 같은 장점을 가진다.

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

❗️ 제네릭 필요성


✏️ 제네릭 사용 전

public static void main(String[] args) {
    List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    int sum = 0;
    for (Object number : numbers) {
        sum += (int) number;
    }
}

List에 있는 모든 숫자를 더하는 로직이다. List에 타입 지정을 안 했기 때문에 Object로 타입이 지정되고 더하는 부분에서 형변환을 직접 해줘야 한다.

위 예제에서는 형 변환을 한 번밖에 안했지만 만약 타입 지정을 안 한 List가 사용되는 곳이 1000군데가 넘는다면 1000군데서 전부 예제처럼 직접 형변환을 해줘야 하는 번거로움이 있다.

또한 만약 값을 잘못 넣어 int형이 아닌 String형의 값을 넣었다면 컴파일 에러가 발생하지 않고 런타임에 ClassCastException 이 터지게 된다.

✏️ 제네릭 사용 후

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    int sum = 0;
    for (Integer number : numbers) {
        sum += number;
    }
}

불필요한 형변환을 안 해도 되고 코드도 더 깔끔해진다.

타입의 안전성을 높일 수 있고 위의 제네릭을 사용하기 전과 다르게 아래와 같이 컴파일 시에 의도하지 않은 타입이 들어오는 걸 미리 막을 수 있다.

💡 제네릭의 선언 및 생성


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

class MyArray<T> {
    
    T element;
    
    void setElement(T element) {
    	this.element = element; 
    }
    
    T getElement() { 
    	return element; 
    }
}

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

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

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

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

MyArray 클래스에 사용된 타입 변수로 Integer 타입을 사용하는 예제

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

위처럼 제네릭 클래스를 생성할 때 사용할 실제 타입을 명시하면, 내부적으로는 정의된 타입 변수가 명시된 실제 타입으로 변환되어 처리된다.

자바에서 타입 변수 자리에 사용할 실제 타입을 명시할 때 기본 타입을 바로 사용할 수는 없다.
이때는 위 예제의 Integer와 같이 래퍼(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>에 추가할 수 있다.

하지만 Sparrow 클래스는 타입이 다르므로 추가할 수 없다.


References
:https://tecoble.techcourse.co.kr/post/2020-11-09-generics-basic/

profile
Fill in my own colorful colors🎨

0개의 댓글