제네릭이 갖는 의미는 ‘일반화’이다. 그리고 자바에서 그 일반화의 대상은 자료형이다.
Box< T> 클래스에서 사용된 T를 가리켜 ‘타입 매개변수(Type Parameter)’라 한다. 메소드의 매개변수와 유사하게 자료형 정보를 인자로 전달받는 형태이기 때문이다. <>안에 전달되는 것을 가리켜 ‘타입 인자(Type Argument)’라 한다. 타입 매개변수 T에 전달되는 인자로 바라보고 그렇게 이름을 지어준 것이다.
Box< Apple> aBox = new Box< Apple>(); -> Box< Apple>을 가리켜 ‘매개변수화 타입(Parameterized Type)’이라 한다. 자료형 Apple이 타입 매개변수 T에 전달되어 Box< Apple>이라는 새로운 자료형이 완성된 것이기 때문에 ‘매개변수화 타입’이라 부른다.
Box< T>에서 T -> 타입 매개변수(Type Parameter)
Box< Apple>에서 Apple -> 타입 인자 (Type Argument)
Box< Apple> -> 매개변수화 타입 (Parameterized Type), 제네릭 타입(Generic Type)
타입 매개변수의 이름은 짓기 나름이지만 일반적으로 두 가지 규칙을 지켜서 이름 짓는다. 한 문자로 이름을 짓는다, 대문자로 이름을 짓는다. 가급적 의미를 두어 이름을 짓는 것이 좋다.
E Element
K Key
N Number
T Type
V Value
‘매개변수화 타입’을 구성할 때 기본 자료형의 이름은 ‘타입 인자’로 쓸 수 없다. int, char .. 기본 자료형에 대한 래퍼 클래스 사용
class Box< T extends Number> (…) -> 인스턴스 생성 시 타입 인자로 Number 또는 이를 상속하는 클래스만 올 수 있음
어떠한 클래스의 인스턴스를 참조하게 될지 알 수 없기 때문에 변수를 통해 호출할 수 있는 메소드는 Object 클래스의 메소드로 제한이 된다. 반면 타입 인자를 제한 하면 제한하는 클래스의 메소드 또한 호출할 수 있다.
제네릭 클래스의 타입 인자를 인터페이스의 이름으로 제한할 수 있다. 그리고 제한할 때에는 클래스와 마찬가지로 extends를 사용한다. 인터페이스를 구현하는 클래스로 타입 인자를 제한하면 인터페이스에 선언되어 있는 메소드의 호출이 가능하게 된다. 타입 인자를 제할할 때에는 하나의 클래스와 하나 이상의 인터페이스에 대해 동시에 제한을 할 수가 있다. class Box<T extends Number & Eatable> {…} -> 이 경우 Number를 상속하면서 동시에 Eatable 인터페이스를 구현하는 클래스만이 타입 인자로 올 수 있다.
제네릭 클래스는 인스턴스 생성 시 자료형이 결정된다. 반면 제네릭 메소드는 ‘메소드 호출시에 자료형이 결정’된다.
Object와 String이 상속 관계에 있더라도 Box< Object>와 Box< String>은 상속 관계를 형성하지 않는 별개의 자료형이다.
상한 제한된 와일드카드(Upper-Bounded Wildcards): Box<? extends Number> box -> box는 Box< T> 인스턴스를 참조하는 참조변수 이다. 단 이때 Box< T> 인스턴스의 T는 Number 또는 이를 상속하는 하위 클래스이어야 함
하한 제한된 와일드카드(Lower-Bounded Wildcards): Box<? super Integer> box -> box는 Box< T> 인스턴스를 참조하는 참조변수이다. 단 이때 Box< T> 인스턴스의 T는 Integer 또는 Integer가 상속하는 클래스이어야 함 (Box< Integer>, Box< Number>, Box< Object>)
매개변수 선언: Box<? extends Toy> box -> box가 참조하는 인스턴스를 대상으로 꺼내는 작업만 허용하겠다는 의미 / box가 참조하는 인스턴스를 대상으로 저장하는 기능의 메소드 호출은 불가능하다.
매개변수 선언: Box<? super Toy> box -> box가 참조하는 인스턴스를 대상으로 넣는 작업만 허용하겠다는 의미 / box가 참조하는 인스턴스를 대상으로 꺼내는 기능의 메소드 호출은 불가능하다.
프레임워크 - 잘 정의된 구조 또는 골격, 잘 정의된 구조의 클래스들의 모임
컬렉션 프레임워크 - 데이터의 저장 방법, 그리고 이와 관련 있는 알고리즘에 대한 프레임워크, 자료구조와 알고리즘을 제네릭 기반의 클래스와 메소드로 미리 구현해 놓은 결과물
List< E> 인터페이스를 구현하는 컬렉션 클래스들(ArrayList< E>, LinkedList< E>)이 갖는 공통적인 특성 두 가지 - 인스턴스의 저장 순서를 유지한다, 동일한 인스턴스의 중복 저장을 허용한다.
List< String> list = new ArrayList<>(); -> 이 문장에서 ArrayList< E>형 참조변수가 아닌 List< E>형 참조변수를 선언한 이유는 코드에 유연성을 제공하기 위함이다. 주로 List< E>에 선언된 메소드를 호출하기 때문에 굳이 ArrayList< E>형 참조변수를 선언할 필요가 없으며, 이렇듯 List< E>형 참조변수로 인스턴스를 참조할 경우 다음과 같이 컬렉션 클래스의 교체가 용이해진다. List< String> list = new ArrayList<>(); -> List< String> list = new LinkedList<>();
ArrayList< E> 인스턴스는 내부적으로 배열을 생성해서 인스턴스를 저장하는데, 필요하면 그 배열의 길이를 스스로 늘리기 때문이다. 단 배열의 길이를 늘린다는 것은 더 긴 배열로의 교체를 의미한다. (한번 생성된 배열은 길이를 늘릴 수 없으므로)
ArrayList< E>
장점: 저장된 인스턴스의 참조가 빠르다.
단점: 저장 공간을 늘리는 과정에서 시간이 비교적 많이 소요된다, 인스턴스의 삭제 과정에서 많은 연산이 필요할 수 있다. 따라서 느릴 수 있다.
LinkedList< E>
장점: 저장 공간을 늘리는 과정이 간단하다, 저장된 인스턴스의 삭제 과정이 단순한다.
단점: 저장된 인스턴스의 참조 과정이 배열에 비해 복잡하다, 따라서 느릴 수 있다.
Iterable< T>를 직접 혹은 간접적으로 구현하는 클래스의 인스턴스를 대상으로 for-each문을 구성할 수 있다.
Set< E> 인터페이스를 구현하는 제네릭 클래스의 특성 두 가지. 저장 순서가 유지되지 않는다. 데이터의 중복 저장을 허용하지 않는다.
인터페이스 내에 선언된 변수는 public, static, final이 선언된 것으로 간주한다.
enum Scale { DO, RE, MI, FA, SO, RA, TI } // 열거 자료형 Scale의 정의
열거형 정의 안에 위치한 이름들을 가리켜 ‘열거형 값’이라 한다. (정확한 명칭은 ‘Enumerated Values’이다) 열거형은 클래스와 성격이 유사하다. 따라서 참조변수의 선언도 가능하다. 단 선언된 참조변수는 해당 열거형 내에 선언된 ‘열거형 값’만 대입이 가능하다. Scale sc = Scale.DO;
기본적으로 ‘열거형 값’은 Scale.DO와 같이 표현하지만, case문에서는 표현의 간결함을 위해 DO와 같이 ‘열거형 값’의 이름만 명시하기로 약속되어 있다.
특정 클래스 내에서만 사용하고자 하는 열거형 값이 있다면, 해당 클래스 내에 열거형을 정의할 수 있다.
‘열거형 값’이 해당 자료형의 인스턴스이다. 열거형 정의에도 생성자가 없으면 디폴트 생성자가 삽입된다. 다만 이 생성자는 private으로 선언이 되어 직접 인스턴스를 생성하는 것이 불가능할 뿐이다. ‘열거형 값’은 각각 인스턴스를 생성한다. 열거형 생성자는 무조건 private으로 선언해야 한다.
열거형 값의 선언에서 소괄호를 통해서 생성자에 인자를 전달할 수 있다. 열거형도 Object 클래스를 상속하는 일종의 클래스이다. 따라서 생성자는 물론, 인스턴스 변수와 메소드 둘 다 가질 수 있다. 다만 모든 생성자를 private으로 선언해야 하기 때문에 ‘열거형 값’이 유일한 인스턴스 생성 방법이라는 차이가 있을 뿐이다.
메소드의 매개변수를 선언할 때 ‘가변 인자 선언’을 하면, 전달되는 인자의 수에 제한을 두지 않을 수 있다.
ex) show(String…vargs) 가변 인자의 매개변수는 배열을 참조한다.
@Override “상위 클래스의 메소드 오버라이딩 또는 인터페이스에 선언된 추상 메소드의 구현입니다” 이에 어긋난 메소드 정의가 이뤄지면 컴파일 오류로 이어져서 우리로 하여금 잘못된 부분을 확인할 수 있게 해준다.
@Deprecated 아직은 호환성 유지를 위해 존재하지만 이후에 사라질 수 있는 클래스 또는 메소드를 가리켜 Deprecated 되었다고 한다.