[Java] 제네릭(Generic)

박채은·2022년 11월 10일
0

Java

목록 보기
29/30
post-custom-banner

제네릭

데이터를 저장하는 클래스가 있다고 생각해보자.
String, int, double 타입의 데이터를 저장하려면 각 타입별로 별도의 클래스를 만들어야 한다.
(stringDataSave, intDataSave, doubleDataSave)

이런 귀찮음을 해결해주는 것이 제네릭이고 단 하나의 클래스로 모든 타입에 적용이 가능하도록 만들어 준다.

✔️ 클래스나 메서드의 코드를 작성할 때, 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해두는 것
=> 작성한 클래스/메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것


제네릭 클래스

= 제네릭이 사용된 클래스

class Basket<T> {
    private T item;

    public Basket(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}
  • T : 타입 매개변수
    • T(Type) 가 아닌 임의의 문자로 지정할 수 있다.
    • K(Key), V(Value), E(Element), N(Number), R(Result) 등

Basket<String>  basket1 = new Basket<>("Hello");
  • new Basket<>에서는 타입 매개변수를 생략해도 상관 없다.
    (그 대신에 참조 변수에는 잘 작성해줘야 함)

주의할 점

1. 제너릭 클래스인 경우에, 클래스 변수/메서드(static)에는 타입 매개변수를 사용할 수 없다.

class Basket<T> {
	private T item1; // O 
	static  T item2; // X 
}

클래스 변수/메서드는 모든 인스턴스가 공유하는 성질이다.

Basket<String>Basket<Integer>를 만든다고 하면, 각각의 static 변수는 String과 Integer 타입이여야 할 것이다.
하지만 static 변수는 Basket 클래스의 모든 인스턴스가 공유해야 하는 값이므로 타입이 달라질 수 없다!

2. 타입 매개변수에 치환될 타입으로 기본 타입을 지정할 수 없다.

  • int, double 같은 원시 타입은 불가
  • Integer, Double 같은 래퍼 클래스만 가능!

제한된 제네릭 클래스

class Basket<T extends A> {}
  • 타입으로 A 클래스의 하위 클래스만 지정되도록 제한
class Basket<T extends B> {}
  • 특정 인터페이스(B)를 구현한 클래스만 지정되도록 제한 => extends 키워드
class Basket<T extends A & B> {}
  • A 클래스의 하위 클래스이면서 특정 인터페이스(B)를 구현한 클래스만 지정되도록 제한
    • & 사용
    • 클래스가 인터페이스보다 앞에 위치해야 한다.

제네릭 메서드

클래스 전체를 제너릭으로 선언하지 않고, 클래스 내부의 특정 메서드만 제너릭으로 선언한 것

class Basket {
		...
		public <T> void add(T element) {
				...
		}
}

주의할 점

class Basket<T> {    // 1 : Class에서 선언한 타입 매개변수 T 
		...
		public <T> void add(T element) { 
        // 2 : 메소드에서 선언한 타입 매개변수 T 
				...
		}
}

1. 클래스에서 선언한 T(1)와 메소드에서 선언한 T(2)는 별개의 것이다!

이는 타입이 지정되는 시점이 다르기 때문이다.
(1번) T는 클래스가 인스턴스화 될 때, 타입이 지정되고
(2번) T는 메서드가 호출될 때, 타입이 지정된다.

Basket<String> basket = new Bakset<>(); // 1의 T가 String으로 지정
basket.<Integer>add(10);                // 2의 T가 Integer로 지정
basket.add(10);                         // 타입 지정을 생략할 수도 있다. 

2. 클래스 타입 매개변수와 달리, 메서드 타입 매개변수는 static 키워드를 붙일 수 있다.

3. 제네릭 메서드는 메서드가 호출되는 시점에 제네릭 타입이 결정되므로, 제네릭 메소드를 정의할 때는 Object 클래스의 메소드만 사용할 수 있다.
(length()와 같은 특정 클래스 내에만 있는 메서드는 사용할 수 없다. 아직 타입이 결정되지 않았기 때문에)


래퍼 클래스

8개의 기본 타입에 해당하는 데이터를 객체로 포장해 주는 클래스를 래퍼 클래스(Wrapper class)라고 한다.

  • java.lang 패키지에 포함됨
  • 산술 연산이 불가능하다.
  • 객체이기 때문에 null로 초기화가 가능하다.
  • 래퍼 클래스로 감싸고 있는 기본 타입 값은 외부에서 변경할 수 없다. 만약 값을 변경하고 싶다면 새로운 포장 객체를 만들어야 한다.


출처: TCPschool

오토 박싱/오토 언박싱

  • 박싱: 기본 타입 -> 래퍼 클래스
  • 언박싱: 래퍼 클래스 -> 기본 타입

JDK 1.5부터는 박싱과 언박싱이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해준다.

Integer num = new Integer(17); // 박싱
int n = num.intValue();        // 언박싱
System.out.println(n);

// 오토 박싱, 오토 언박싱
Character ch = 'X'; // Character ch = new Character('X');
char c = ch;        // char c = ch.charValue();
System.out.println(c);

참고-1
참고-2

값 비교

public class Main {
    public static void main(String[] args)  {
        Integer num1 = new Integer(10); // 래퍼 클래스1
        Integer num2 = new Integer(10); // 래퍼 클래스2
        int i = 10; // 기본 타입
		 
        System.out.println("래퍼클래스 == 기본타입 : "+(num1 == i)); // true
        System.out.println("래퍼클래스.equals(기본타입) : "+num1.equals(i)); // true
        System.out.println("래퍼클래스 == 래퍼클래스 : "+(num1 == num2)); // false
        System.out.println("래퍼클래스.equals(래퍼클래스) : "+num1.equals(num2)); // true
    }
}
  • 래퍼 클래스끼리의 비교: 둘 다 객체이기 때문에
    • == : 주소 값 비교
    • equals() : 내부 값 비교
  • 래퍼 클래스와 원시 타입 데이터의 비교: 컴파일러가 오토 박싱/언박싱을 해주기 때문에 ==equals() 모두 내부 값을 비교한다.

와일드 카드

  • ? 기호로 사용
  • 어떤 타입이로든 대체될 수 있는 타입 파라미터
  • <?> = <? extends Object> = 모든 클래스 타입이 가능
  • <? extends T> : T와 T를 상속받는 하위 클래스 타입만 파라미터로 제한
  • <? super T> : T와 T의 상위 클래스 타입만 파라미터로 제한
post-custom-banner

0개의 댓글