관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch12 - 15 ~ 22 [제네릭형변환, 열거형, 열거형의 조상, 열거형에 멤버 추가하기] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch12 - 15 ~ 22 [제네릭형변환, 열거형, 열거형의 조상, 열거형에 멤버 추가하기]

소소한나구리 2023. 12. 14. 10:41

1) 제네릭타입의 형변환

  • 제네릭타입과 원시 타입간의 형변환은 바람직 하지 않음 (가능 하지만 경고 발생)
  • JDK1.5 이후부터는 원시타입을 쓰는 것 자체가 바람직 하지 않으며 서로 다른타입이 대입된 제네릭타입은 형변환이 불가함
  • 와일드카드가 사용된 제네릭 타입으로는 형변환 가능하며 와일드카드 문장에 따라 달라지며 경고가 발생할 수 있음

(1) 예시

Box<Object> objBox = null;
Box box = (Box)Box<Object>;	// OK. 제네릭타입 -> 원시타입 / 경고 발생
objbox = (Box<Object>)Box;	// OK. 원시타입 -> 제네릭타입 / 경고 발생

Box<Object> objBox = null;
Box<String> strBox = null;
objBox = (Box<Object>)strBox;	//에러. 서로 다른타입이 대입된 제네릭타입끼리의 형변환 불가
strBox = (Box<String>)objBox;	//에러.

// 와일드 카드가 사용된 제네릭타입으로의 형변환은 가능
// Object와 그의 자손들 사용 가능
Box<? extends Object> wBox = (Box<? extends Object>)new Box<String>();	// OK
Box<? extends Object> wBox = new Box<String>();	//위 문장과 동일(형변환 생략)

// 와일드 카드가 사용된 제네릭스 클래스 참조변수로 형변환
FruitBox<? extends Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<? extends Fruit> appleBox = new FruitBox<Apple>();

// 와일드 카드를 사용된 제네릭스 클래스 참조변수를 형변환
FruitBox<Apple> appleBox2 = (FruitBox<Apple>)appleBox;	// OK. 경고 발생

 

2) 제네릭 타입의 제거

  • 컴파일러는 제네릭 타입을 제거한 뒤에 필요한 곳에 형변환을 넣음(하위 호환성 때문에 이렇게 작동함)
  • C#언어는 컴파일 때 제네릭타입을 유지 (사실 유지하는 것이 더 유리함 - 성능, 런타임때 제네릭 확인 등등)

(1) 컴파일 시 제네릭 타입이 제거되는 과정

  • 1. 제네릭 타입의 경계(bound)를 제거

  • 2. 제네릭 타입 제거 후에 타입이 불일치(형변환이 생략되어 있을 때)하면, 형변환을 추가

  • 와일드 카드가 포함된 경우, 적절한 타입으로 알아서 형변환을 추가


3) 열거형(enum)

  • 서로 관련된 상수들을 같이 묶어 놓은 것
  • 정의한 순서대로 번호가 0부터 자동으로 부여 됨

클래스의 상수를 enum으로 변경한 예제

  • C언어는 값만 체크하는 것에 비해 Java는 값과 타입을 모두 체크하므로 타입에러에 안전한 열거형을 제공함
if(Card.kind.CLOVER==Card.Value.TWO) {	//컴파일에러 - 타입이 달라서 비교 불가

 

(1) 열거형의 정의와 사용

  • 열거형을 정의하고 사용하는 방법
// enum 열거형이름 { 상수명1, 상수명2, ... } 으로 정의
enum Direction { EAST, SOUTH, WEST, NORTH}

// enum 사용
class Unit {
	int x, y;
    Direction dir; // 열거형 타입의 변수를 선언
    
    void init() {
    	dir = Direction.EAST;	// init 메서드 호출 시 열거형의 EAST를 참조변수에 저장
    }
}

 

(2) 열거형 상수 비교

  • == 와 compareTo()사용 가능하며 비교연산자(>, < 등)는 사용 불가
  • compareTo(): 비교 값이 같으면 0, 왼쪽의 갑이 크면 양수, 오른쪽의 값이 크면 음수

 

(3) 열거형의 조상 - java.lang.Enum(클래스)

  • 모든 열거형은 Enum의 자손으로 메서드를 상속 받음
  • ordinal()은 상수가 정의된 순서이지 상수가 정의된 값과는 무관함

(4) 메서드

  • Class<E> getDeclaringClass() : 열거형의 Class 객체를 반환함
  • String name() : 열거형 상수의 이름을 문자열로 반환함
  • int ordinal() : 열거형 상수가 정의된 순서를 반환함(0부터 시작)
  • T valueOf(Class<T> enumType, String name): 지정된 열거형에서 name과 일치하는 열거형 상수를 반환함
  • T[] : values() : 정의한 모든 상수를 배열로 반환함
  • values(), valuesOf()는 컴파일러가 자동으로 추가해줌
//values(): 가지고있는 모든 상수를 배열로 반환함
Direction[] dArr = Direction.values();

// 이름과 순서를 for문으로 출력
for(Direction d : dArr)
	System.out.printf("%s=%d%n", d.name(), d.ordinal());
    
//ValuesOf사용 예
Direction d = Direction.valueOf("WEST"); // 열거형 상수를 d에 저장
Direction d = Direction.WEST             // 위와 동일함

 

(5) 예제

//                0      1      2      3
enum Direction { EAST, SOUTH, WEST, NORTH }	// 열거형 상수는 기본형이 아니라 객체

class Ex12_5 {
	public static void main(String[] args) {
		// 열거형 상수를 저장하는 여러 방법
		Direction d1 = Direction.EAST;	// 열거형타입.상수이름
		Direction d2 = Direction.valueOf("WEST");	//문자열 이용
		Direction d3 = Enum.valueOf(Direction.class, "EAST");	// 최고조상의 valueOf를 이용

		System.out.println("d1="+d1);
		System.out.println("d2="+d2);
		System.out.println("d3="+d3);

		System.out.println("d1==d2 ? "+ (d1==d2));
		System.out.println("d1==d3 ? "+ (d1==d3));
		System.out.println("d1.equals(d3) ? "+ d1.equals(d3));	// 객체라서 equals 사용 가능
//		System.out.println("d2 > d3 ? "+ (d1 > d3)); // 에러. 비교연산자사용 불가(객체라서)
		System.out.println("d1.compareTo(d3) ? "+ (d1.compareTo(d3)));
		System.out.println("d1.compareTo(d2) ? "+ (d1.compareTo(d2)));

		switch(d1) {
			case EAST: // Direction.EAST라고 쓰지않고 상수만 입력 - case문의 문법임
				System.out.println("The direction is EAST."); break;
			case SOUTH:
				System.out.println("The direction is SOUTH."); break;
			case WEST:
				System.out.println("The direction is WEST."); break;
			case NORTH:
				System.out.println("The direction is NORTH."); break;
			default:
				System.out.println("Invalid direction."); break;
		}
		
		// 열거형의 모든 상수를 배열로 반환
		Direction[] dArr = Direction.values();

		for(Direction d : dArr)  // for(Direction d : Direction.values()) 
			System.out.printf("%s=%d%n", d.name(), d.ordinal()); 
	}
}

/*
출력값
d1=EAST
d2=WEST
d3=EAST
d1==d2 ? false
d1==d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2
The direction is EAST.
EAST=0
SOUTH=1
WEST=2
NORTH=3
*/

 

4) 열거형에 멤버 추가하기

  • 불연속적인 열거형 상수의 경우 원하는 값을 상수의 괄호() 안에 작성하여 멤버를 추가할 수 있음
  • 개수와 타입의 제한이 없이 입력할 수 있음
// 정의된 순서는 0,1,2,3
enum Direction { EAST(1, ">"), SOUTH(5), WEST(-1), NORTH(10)}

 

(1) 괄호()를 사용하려면 인스턴스 변수와 생성자가 필요함

  • 열거형의 각 상수에 정의된 멤버를 사용하기 위해서는 변수를 선언하여 생성자로 변수에 값을 입력하는 방식으로 사용해야함
  • 열거형의 생성자는 묵시적으로 private이므로 외부에서 객체 생성이 불가능함

(2) 예제

  • name(): 정의된 상수의 이름을 출력
  • getValue(), getSymbol() : 상수의 멤버를 출력할 수 있도록 메서드를 작성
enum Direction2 { // 열거형 선언 
	EAST(1, ">"), SOUTH(2,"V"), WEST(3, "<"), NORTH(4,"^");

	private static final Direction2[] DIR_ARR = Direction2.values();
	private final int value;	// 첫번째 값 저장
	private final String symbol;	// 두번째 값 저장

	// 생성자 생성
	Direction2(int value, String symbol) { // 접근 제어자 private이 생략됨
		this.value  = value;
		this.symbol = symbol;
	}

	public int getValue()     { return value;  }
	public String getSymbol() { return symbol; }

	public static Direction2 of(int dir) {
        if (dir < 1 || dir > 4) // 0 ~ 3범위가 벗어나면 예외가 발생하도록 작성
            throw new IllegalArgumentException("Invalid value :" + dir);

        return DIR_ARR[dir - 1];
	}	

	// 방향을 회전시키는 메서드. num의 값만큼 90도씩 시계방향으로 회전
	public Direction2 rotate(int num) {
		num = num % 4;	// 큰값을 입력하여도 회전

		if(num < 0) num +=4; // num이 음수일 때는 시계반대 방향으로 회전 

		return DIR_ARR[(value-1+num) % 4];
	}

	public String toString() {
		return name()+getSymbol();
	}
} // enum Direction2

class Ex12_6 {
	public static void main(String[] args) {
		for(Direction2 d : Direction2.values())
			//ordinal로 하면 입력된 순서가 출력 
			System.out.printf("%s=%d%n", d.name(), d.getValue()); // 상수의 값을 출력
		
		Direction2 d1 = Direction2.EAST;
		Direction2 d2 = Direction2.of(2);

		System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
		System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
		System.out.println(Direction2.EAST.rotate(1));
		System.out.println(Direction2.EAST.rotate(2));
		System.out.println(Direction2.EAST.rotate(-1));
		System.out.println(Direction2.EAST.rotate(-2));
	}
}

/*
출력값
EAST=1
SOUTH=2
WEST=3
NORTH=4
d1=EAST, 1
d2=SOUTH, 2
SOUTHV
WEST<
NORTH^
WEST<
*/

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의