데이터를 저장하는 클래스가 있다고 생각해보자.
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)라고 한다.
출처: 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);
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의 상위 클래스 타입만 파라미터로 제한