🦁아기사자

[Java] 제네릭

코딩하는 하마 2025. 3. 31. 13:50

제네릭 사용법

제네릭은 잘못된 타입이 사용 될 수 있는 문제를 컴파일 과정에서 제거할 수 있다. 자바 컴파일러는 코드에서 잘못 사용된 타입 때문에 발생핳는 문제점을 제거하기 위해 제네릭 코드에 대해 강한 타입 체크를 한다. 그럼 제네릭 사용법에 대해 알아보겠다.

 

[제네릭 사용법]

public class 클래스명 <T> {...}
public interface 인터페이스명<T> {...}

 

제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다. 위 코드와 같이 클래스 또는 인터페이스 이름 뒤에 <> 부호가 붙고 사이에 타입 파라미터가 위치한다. 또한 멀티 타입 파라미터 사용도 가능하다. 

 

[멀티 타입 파라미터 사용법]

class 클래스명 <k,v> {...}

 

[타입 파라미터]

꺾새 사이에 들어가는 타입 파라미터의 종류에 대해 살펴보자. 

<T> Type
<E> Element
<K> Key
<N> Number
<V> Value
<R> Result

 

ex)

// 제네릭 클래스 
class Animal <T> {
private T t;

public void setT(T t)
{
	this.t = t;
}

public T getT() {
	return t;
  }
}
// 제네릭 인터페이스
interface Animal <T> {
    T example();
}
class ex implements Animal<String>{
    @Override
    public String example() {
        return null;
    }
}
// 멀티 타입 파라미터 사용
import java.util.Map;

class exam <K, V> implements Map.Entry<K,V>{
    private  K key;
    private V value;

    @Override
    public K getKey(){
        return this.key;
    }

    @Override
    public V getValue() {
        return this.value;
    }

    @Override
    public V setValue(V value) {
        this.value = value;
        return value;
    }
}

제네릭 주요 개념 (바운디드 타입, 와일드 카드)

1) 바운디드 타입 

이는 제네릭 타입의 범위를 제한 할 수 있다. 제네릭 타입에 extends를 사용하면 특정 타입의 자손들만 대입할 수 있게 제한이 가능하다. 여기서 extends는 클래스의 extends 또는 인터페이스의 implements 처럼 구현한다는 것은 아니다. 

예제를 살펴보겠다. 

 

ex) 

class Test <T extends Number > {
    T element;
    void setTest(T element) {
        this.element = element;
    }
    T getElement() {
        return element;
    }
}

 

 

2) 와일드 카드

와일드 카드는 이름에 제한을 두지 않음을 표현하는 데 사용되는 기호를 의미한다. 와일드 카드에는 총 세 가지 형태가 있으며 (?)라는 키워드로 표현된다. 

제네릭타입<?> 은 타입 파라미터를 대치하는 것으로 모든 클래스나 인터페이스 타입이 올 수 있다.

제네릭타입<? extends 상위타입> 와일드카드의 범위를 특정 객체의 하위 클래스만 올 수 있다. (bounded)
제네릭타입<? super 하위타입>  와일드카드의 범위를 특정 객체의 상위 클래스만 올 수 있다. (bounded)
제네릭타입<? extends A & B & C>  와일드카드의 범위를 명시된 다수 객체의 하위 클래스로 제한한다.

 

ex)

import java.util.ArrayList;

class Animal {
    public void cry() {
        System.out.println("동물");
    }
}
class Cat extends Animal{
    public void cry() {
        System.out.println("냐옹");
    }
}
class Dog extends Animal {
    public void cry() {
        System.out.println("멍멍");
    }
}
class WildCard<T>{
    ArrayList<T> a  = new ArrayList<T>();
    public static void crying(WildCard<? extends Animal > list) {
        Animal b = list.get(0);
        b.cry();
    }
    void add(T animal ){
        a.add(animal);
    }
    T get (int index) {
        return a.get(index);
    }
}

public class Main {
    public static void main(String[] args) {
        WildCard<Cat> catList = new WildCard<Cat>();
        WildCard<Dog> dogList = new WildCard<Dog>();
        
        catList.add(new Cat());
        dogList.add(new Dog());
        
        WildCard.crying(catList);
        WildCard.crying(dogList);
    }
}

제네릭 메소드 만들기

제네릭 메소드는 메소드의 선언부 타입 변수를 사용한 메소드로 제네릭 메소드 형태는 다음과 같다. 

 

public static <T> void method() {}

 

ex)

// 제네릭 메소드
class Animal<T, M> {
    private T name;
    private M sound;

    Animal ( T name , M sound){
        this.name = name;
        this.sound = sound;
    }

    public T getName() {
        return name;
    }

    public void setName (T name){
        this.name = name;
    }
    public M getSound(){
        return sound;
    }

    public void setSound( M sound){
        this.sound = sound;
    }

    public static <T,V> boolean compare(Animal<T,V> p1, Animal<T,V> p2){
        boolean nameCompare = p1.getName().equals(p2.getName());
        boolean soundCompare =p1.getSound().equals(p2.getSound());
        return nameCompare && soundCompare;
    }

}

public class Main
    public static void main(String []args){
        
        Animal<String,Integer> p1 = new Animal(String,Integer>("cat",111);
      
        Animal<String,Integer> p2 = new Animal("hippo",222);
      
        boolean result = p1.compare(p1,p2);
        System.out.println(result);
    }
}

Erasure

type erasure : 컴파일 타입에만 타입 제약 조건을 정의하고, 런타임에는 타입을 제거한다. 즉 컴파일 이후에는 제네릭 타입이 존재하지 않고 , Object 또는 상한 제한 (bound)으로 변환된다. 이를 통해 매개변수화 된 타입에 대해 새로운 클래스가 생성되지 않게 하여 런타임 오버헤드 발생을 막는다. 

 

ex) 
타입 소거 전후를 비교해보겠다. 

 

[제네릭을 사용한 코드] 

class Animal<T> {
    private T name;

    public void setName( T name){
        this.name = name;
    }
    public Object getName() {
        return name;
    }
}

 

[컴파일 이후 변환된 코드(타입 소거)]

 

class Animal
    private Object name;

    public void setName( Object name){
        this.name = name;
    }
    public Object getName() {
        return name;
    }
}

 

위 결과를 살펴보면 T가 Object로 변환되는 것을 볼 수 있다. 제네릭 타입이 사라져 런타임에는 타입 정보가 존재하지 않게 된다. 

 


[참고 자료]

https://coding-factory.tistory.com/573

 

[Java] 제네릭(Generic) 사용법 & 예제 총정리

제네릭을 사용해야하는 이유 제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있기 때문입니다. 자바 컴파일러는 코드에서 잘못 사용된 타입 때문

coding-factory.tistory.com

https://velog.io/@bokimy/Java%EC%A0%9C%EB%84%A4%EB%A6%AD-%EC%82%AC%EC%9A%A9%EB%B2%95

 

[Java]제네릭 사용법

제네릭(Generic) 클래스나 메서드의 코드를 작성할 때, 타입을 정해두는 것이 아니라 추후에 지정할 수 있도록 일반화해두는 것. = 작성한 클래스나 메서드의 코드가 특정 데이터 타입에 얽매이지

velog.io

https://xcv20123.tistory.com/39

 

9주차 : 제네릭

제네릭 사용법 ■ 제네릭이란? Java에서 데이터 타입을 일반화하는 것을 의미 제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시 지정 ■ 제네릭을 사용하는 이유 클래스나 메

xcv20123.tistory.com

https://acver.tistory.com/entry/JAVA-%EC%A0%9C%EB%84%A4%EB%A6%AD

 

[JAVA] 제네릭

제네릭 사용법 제네릭을 사용해야 하는 이유 제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있다. 제네릭 코드를 사용하면 타입을 국한하기 때

acver.tistory.com

 

 

'🦁아기사자' 카테고리의 다른 글

[Java] 람다식  (0) 2025.04.01
[Java] I/O  (0) 2025.03.31
[Java] 애노테이션  (0) 2025.03.27
[Java] Enum  (0) 2025.03.26
[Java] 멀티스레드 프로그래밍  (0) 2025.03.25