[Java] Generic

jinsung·3일 전

Java

목록 보기
4/8
post-thumbnail

1. 제네릭이 필요한 이유

제네릭을 사용하면 코드 재사용과 타입 안전성을 확보할 수 있다.

public class Box<T> {

	private T value;
    
    public void setValue(T value) {
    	this.value = value;
    }
    
    public T getValue() {
    	return value;
    }
}
  • <>를 사용한 클래스를 제네릭 클래스라고 한다.

  • 제네릭 클래스를 사용할 땐 타입을 미리 결정하지 않는다.

  • <T> 의 T를 타입 매개변수라고 하고, 이후에 Integer, String 같은 타입으로 변한다.

  • 클래스 내부에 T 타입이 필요한 곳에 T value와 같이 타입 매개변수를 적어두면 된다.

Box<Integer> integerBox = new Box<>(); // Integer로 생성
integerBox.setValue(10);
Integer integer = integerBox.getValue();

Box<String> stringBox = new Box<>(); // String으로 생성
stringBox.setValue("hello");
String string = stringBox.getValue();

System.out.println(integer); //10
System.out.println(string); // "hello"
  • Generic 타입은 생성 시점에 <> 사이에 원하는 타입을 지정한다.

2. 제네릭 용어와 관례

제네릭의 핵심은 사용할 타입을 미리 결정하지 않는다는 점이다.
실제 생성 시점에 타입을 결정하는 것이다.

용어 정리

제네릭(Generic) 단어

  • 제네릭이라는 단어는 일반적인, 범용적인이라는 영어 단어 뜻이다.

  • 풀어보면 특정 타입에 속한 것이 아니라 일반적으로, 범용적으로 사용할 수 있다는 뜻이다.

제네릭 타입(Generic Type)

  • 클래스나 인터페이스를 정의할 때 타입 매개변수를 사용하는 것을 말한다.

  • 제네릭 클래스, 제네릭 인터페이스를 모두 합쳐서 제네릭 타입이라 한다.

  • 타입은 클래스, 인터페이스, 기본형(int 등)을 모두 합쳐서 부르는 말이다.

  • 예: class Box<T> { private T t; }

  • 여기에서 Box<T> 를 제네릭 타입이라 한다.

타입 매개변수(Type Parameter)

  • 제네릭 타입이나 메서드에서 사용되는 변수로, 실제 타입으로 대체된다.
  • 예: Box<T>
  • 여기에서 T 를 타입 매개변수라 한다.

타입 인자(Type Argument)

  • 제네릭 타입을 사용할 때 제공되는 실제 타입이다.

  • 예: Box<Integer>

  • 여기에서 Integer 를 타입 인자라 한다.

제네릭 명명 관례

타입 매개변수는 일반적인 변수명처럼 소문자로 사용해도 문제는 없다. 하지만 일반적으로 대문자를 사용하고 용도에 맞는 단어의 첫글자를 사용하는 관례를 따른다.

  • E : Element
  • K : Key
  • N : Number
  • T : Type
  • V : Value
  • S, U, V etc : 2nd, 3rd, 4th types

여러 타입 매개변수

class KeyValue<K, V> {}

3. 타입 매개변수 제한

타입 매개 변수를 특정 타입으로 제한할 수 있다.

public class ColorPlay<T extends Color> {...}
  • <T extends Color> 을 통해 타입 매개변수 T를 Color과 그 자식들만 받을 수 있도록 제한한다.

  • 자바 컴파일러는 T의 상한이 Color이라고 범위를 예측할 수 있다.

  • 제네릭에 타입 매개변수 상한을 사용해 타입 안전성을 지키면서 상위 타입의 원하는 기능까지 사용할 수 있다.


4. 제네릭 메서드

public class Box {

    public static <T> T genericMethod(T t) {
        return t;
    }
}

// main
Integer integer = Box.<Integer>genericMethod(10);
  • 제네릭 메서드는 메서드를 호출할 때 타입 인자를 전달해서 타입을 지정하는 것이다.

  • Box.<Integer>genericMethod(10); 처럼 타입을 지정하면서 메서드를 호출한다.

타입 매개변수 제한

제네릭 메서드도 제네릭 타입과 같이 타입 매개변수를 제한할 수 있다.

public static <T extends Number> T numberMethod(T t) {}

제네릭 메서드 타입 추론

Integer integer = Box.genericMethod(10);
Integer integer = Box.genericMethod(10.1); //error
  • 제네릭 메서드 호출마다 타입 인자를 계속 전달하는 것은 매우 비효율이다.

  • 자바 컴파일러는 호출 시 인자를 통해 제네릭 메서드의 타입을 추론한다.


5. 와일드 카드

와일드 카드는 제네릭 타입을 더 편리하게 사용할수 있도록 도와준다.

public class WildCard {

	static void printWildcardV1(Box<?> box) {
        System.out.println("? = " + box.getValue());
    }

    static void printWildcardV2(Box<? extends Animal> box) {
        Animal animal = box.getValue();
        System.out.println("이름 = " + animal.getName());
    }

    static Animal printAndReturnWildcard(Box<? extends Animal> box) {
        Animal animal = box.getValue();
        System.out.println("이름 = " + animal.getName());
        return animal;
    }
}
  • 와일드 카드는 ? 를 사용해서 정의한다.

  • 와일드 카드는 이미 만들어진 제네릭 타입을 활용할 때 사용한다.

  • 와일드 카드는 일반 메서드에 사용할 수 있고 단순히 매개변수로 제네릭 타입을 받을 수 있다.

상한 와일드 카드

static void printWildcardV2(Box<? extends Animal> box) {
    Animal animal = box.getValue();
    System.out.println("이름 = " + animal.getName());
}

제네릭 메서드와 마찬가지로 와일드카드에도 상한 제한을 둘 수 있다.

타입 매개변수가 꼭 필요한 경우

와일드카드는 제네릭을 정의할 때 사용하는 것이 아니다.
만약 제네릭 타입으로 반환하고 싶다면 제네릭 메서드를 사용해야 하며, 와일드카드를 사용하면 전달한 타입을 명확하게 반환할 수 없다.


6. 타입 이레이저

이레이저는 지우개라는 뜻이다.

제네릭은 자바 컴파일 단계에서만 사용되고, 컴파일 이후에는 제네릭 정보가 삭제된다.
제네릭에 사용한 타입 매개변수가 모두 사라지는 것이다.

class EraserBox<T> {
	public boolean instanceCheck(T param) {
    	return param instanceof T;
    }
    
    public void create() {
    	return new T();
    }
}
  • 여기서 T 는 런타임에 모두 Object가 되어버린다.

  • 이렇게 되면 instananceof 는 항상 참이 되는 문제가 발생한다.

  • new T() 도 항상 new Object가 되어 버린다.

  • 따라서 자바는 타입 매개변수에 instanceof new 를 허용하지 않는다.

profile
Backend Engineer

0개의 댓글