[자바의정석]Chapter 12. 지네릭스, 열거형, 애너테이션

seungwon·2023년 1월 26일
0

자바의 정석

목록 보기
12/14

1. 지네릭스(Generics)

다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크(compile-time type check)를 해주는 기능

  • 타입 안정성 제공
  • 코드가 간결해짐(타입체크/형변환 생략 가능)

1.2 지네릭 클래스의 선언

클래스명<T>
T : 임의의 참조형 타입 의미

ex)

class Box<T> { // 지네릭 타입 T를 선언 
	T item;
	void setItem(T item) { 
    	this.item = item; 
	} 
    T getltem() { 
    	return item; 
	}
}

👽 generic 사용 불가능한 경우

  • static 멤버
    • 지네릭 변수는 인스턴스변수로 간주되나 static 멤버는 인스턴스변수 참조 불가능
    • 타입의 종류에 관계없이 동일한 것이어야 함 ex) Box<Apple>.itemBox<Grape>.item이 다른 것이어서는 안 됨
  • 지네릭 타입의 배열 생성
    • 지네릭 배열 타입의 참조변수 선언은 가능 (ex)T[] arr;
    • 배열 생성은 불가능 (ex) T[] Arr = new T[temp.length]; // 에러
      (∵ 컴파일시에 T 가 어느 타입인지 알 수 없음)
  • instanceof

1.3 지네릭 클래스의 객체 생성과 사용

☑️ 참조변수 타입 = 생성자에 대입된 타입
상속관계에 있는 클래스 간에도 호환 불가능

Box<Apple> appleBox = new Box<Apple> () ; // OK
Box<Apple> appleBox = new Box<Grape> () ; // 에러(Apple-Grape 두 클래스가 상속관계에 있더라도)

☑️ (JDK1.7~) 추정이 가능한 경우 타입 생략 가능

Box<Apple> appleBox = new Box<Apple>();
Box<Apple> appleBox = new Box<> (); // OK. JDK1.7부터 생략가능

☑️ 메서드의 매개변수로는 상속관계에 있는 클래스의 객체 사용 가능

//Fruit : 부모 클래스, Apple : Fruit의 자식 클래스
Box<Fruit> fruitBox = new Box<Fruit> () ;
fruitBox.add (new Fruit ()); // OK.
fruitBox.add (new Apple ()); // OK. void add (Fruit item)

1.4 제한된 지네릭 클래스

  • 타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한 : 상속 활용
    classFruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정가능
        ArrayList<T> list = new ArrayList<T>() ;
        ...
    }
  • 인터페이스
    class FruitBox<T extends Eatable> { ... }
  • & 기호 : 상속 & 인터페이스 구현
    class FruitBox<T extends Fruit & Eatable> { ... }

1.5 와일드 카드

기호의미
<? extends T>와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T>와일드 카드의 하한 제한. T와 그 조상들만 가능
<?>제한 없음. 모든 타입이 가능. <? extends Object>와 동일

1.6 지네릭 메서드

메서드의 선언부에 지네릭 타입이 선언된 메서드
(지네릭 클래스가 아닌 클래스에도 정의될 수 있음, static메서드에도 사용 가능)

static <T> void sort(List<T> list, Comparator<? super T> c)

static Juice makeJuice (FruitBox<? extends Fruit> box) {
	String tmp = "";
	for (Fruit f : box.getList ()) 
    	tmp += f + " "
	return new Juice (tmp) ;
}

\qquad ⬇️

static <T extends Fruit> Juice makeJuice (FruitBox<T> box) {
	String tmp = "";
	for (Fruit f: box.getList () ) 
    	tmp += f + " "; 
	return new Juice (tmp) ;
}

\\
\\

☑️ 호출시에 타입변수에 타입을 대입

FruitBox<Fruit> fruitBox = new FruitBox<Fruit> () ;
FruitBox<Apple> appleBox = new FruitBox<Apple> () ;
...
System.out.println (Juicer.<Fruit>makeJuice (fruitBox));
System.out.printIn (Juicer.<Apple>makeJuice (appleBox));

ex)

1.7 지네릭 타입의 형변환

  • generic <-> non-generic 간의 형변환 : 항상 가능, 경고 발생

    BoX box = null;
    Box<Object> objBox = null;
    
    box = (Box) objBox; // OK. 지네릭 타입 -> 원시 타입. 경고 발생
    objBox = (Box<Object>) box; // OK. 원시 타입 -> 지네릭 타입. 경고 발생
  • 대입된 타입이 다른 지네릭 타입 간의 형변환

    Box<Object> objBox = null;
    Box<String> strBox = null;
    
    objBox = (Box<Object>) strBox; // 에러. Box<String> -> Box<Object>
    strBox = (Box<String>) objBox; // 에러. Box<Object> -> Box<String>
  • 대팁된 타입이 서로 상속관계에 있는 경우

Box<? extends Object〉 wBox = new Box<String>(); // OK

FruitBox<? extends Fruit> box = new FruitBox<Fruit> () ; // OK
FruitBox<? extends Fruit> box = new FruitBox<Apple> () ; // OK
FruitBox<? extends Fruit> box = new FruitBox<Grape> () ; / / OK
Optional<?> wopt = new Optional<Object>();
Optional<Object> oopt = new Optional<Object>() ;

Optional<string> sopt = (Optional<String>)wopt; // OK. 형변환 가능
Optional<String> sopt = (Optional<String>)oopt; // 에러. 형변환 불가
  • 와일드 카드가 사용된 지네릭 타입간의 형변환
FruitBox<? extends Object> objBox = null;
FruitBox<? extends String> strBox = null;

strBox = (FruitBox<? extends string>)objBox; // OK. 미확정 타입으로 형변환 경고 
ObjBox = (FruitBox<? extends Object>)strBox; // OK. 미확정 타입으로 형변환 경고

1.8 지네릭 타입의 제거

☑️ 컴파일러의 작업 순서 : 지네릭 타입을 이용해서 소스파일 체크 -> 필요한 곳에 형변환 삽입 -> 지네릭 타입 제거

☑️ 지네릭 타입 제거 이유 : 지네릭 도입 이전의 소스 코드와 호환성을 유지하기 위해서

☑️ 지네릭 타입 제거 과정
\quad 1. 지네릭 타입의 경계(bound)를 제거 : <T extends Fruit> -> T를 Fruit으로 대체
\quad 2. 지네릭 타입을 제거한 후에 타입이 일치하지 않으면 형변환을 추가


2. 열거형(enums)

2.1 열거형이란?

서로 관련된 상수를 편리하게 선언하기 위한 것

여러 상수를 정의할 때 사용하면 유용

class Card {
enum Rind { CLOVER, HEART, DIAMOND, SPADE } // 열거형 Kind를 정의
enum Value { TWO, THREE, FOUR } //열거형 Value를 정의

final Kind kind;// 타입이 int가 아닌 Kind임에 유의
final Value value;

☑️ 타입에 안전한 열거형(typesafe enum) : 값이 같아도 타입이 다르면 컴파일 에러 발생

if (Card.CLOVER == Card.TWO) // OK
if (Card.Kind.CLOVER == Card.Value.TWO) // 컴파일 에러. 값은 같지만 타입이 다름

2.2 열거형의 정의와 사용

enum 열거형이름{ 상수명1, 상수명2, ...}

모든 열거형의 조상 - java.lang.Enum

2.3 열거형에 멤버 추가하기

2.4 열거형의 이해

각 열거형 상수는 각각의 객체

ex)

enum Direction { EAST, SOUTH, WEST, NORTH }

=

class Directionstatic final Direction EAST = new Direction ("EAST");
    static final Direction SOUTH = new Direction ("SOUTH");
    static final Direction WEST = new Direction ("WEST"');
    static final Direction NORTH = new Direction ("NORTH");

	private String name;
    
    private Direction (String name) {
	  this.name = name;
	}
}    

3. 애너테이션(annotation)

3.1 애너테이션이란?

프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것

== 주석(프로그래밍 언어에 영향x)

3.2 표준 애너테이션

3.3 메타 애너테이션

애너테이션을 위한 애너테이션(=dㅐ너테이션에 붙이는 애너테이션)

애너테이션을 정의할 때 애너테이션의 적용대상(target)이나 유지기간(retention)등을 지정하는데 사용

  • @Target : 애너테이션에 적용할 수 있는 대상
  • @Retention : 애너테이션이 유지(retention)되는 기간을 지정(소스파일, 클래스 파일, 실행시 등)
  • @Documented : 애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다
  • @lnherited : 애너테이션이 자손 클래스에 상속되도록 한다
  • @Repeatable : 보통은 하나의 대상에 한 종류의 애너테이션을 붙이는데,‘@Repeatable’이 붙은 애너테이션은 여러 번 붙일 수 있다.
  • @Native : 네이티브 메서드(native method)에 의해 참조되는 ‘상수 필드(constant field)’에 붙이는 애너테이션

3.4 애너테이션 타입 정의하기

@interface 애너테이션이름 {
	타입 요소이름(); //애너테이션의 요소를 선언
    ...
}

*애너테이션에도 인터페이스처럼 상수를 정의할 수 있지만.디폴트 메서드는 정의할 수 없다.

👽 애너테이션의 요소

애너테이션 내에 선언된 메서드

  • 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용된다.
  • ()안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다
  • 요소를 타입 매개변수로 정의할 수 없다.
@interface AnnoTest {
	int id = 100; // OK. 상수선언. static final int id = 100; 
    String major(int i, int j); // 에러. 매개변수를 선언할 수없음
	String minor() throws Exception; // 에러. 예외를 선언할 수 없음 
    ArrayList<T> list(); // 에러. 요소의 타입에 타입 매개변수 사용불가
}

👽 마커 애너테이션

요소가 하나도 정의되지 않은 애너테이션(Serializable이나 Cloneable 인터페이스)

0개의 댓글