ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] 제네릭스(Generics) / 열거형 (Enum)
    DEV/JAVA 2024. 4. 3. 18:36

    1) 제네릭스란?

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

    → 의도하지 않은 타입의 객체가 저장되는것을 막아주고, 저장된 객체를 꺼낼때 원래 타입과 다른 타입으로 잘못 형변환 되어 발생할 수 있는 오류를 줄여준다.

     

    장점

    1. 타입 안정성 제공
    2. 타입체크와 형변환을 생략하여 코드가 간결해짐

    2) 사용 방법

    : 클래스와 메서드에 선언 가능

    • 지네릭 클래스 예시
    //일반 클래스
    class Box {
    	Object item;
    	
    	void setItem(Object itme) { this.item = item; }
    	Object getItem() { return item; }
    }
    
    //지네릭 클래스로 변경
    class Box<T> { //지네릭 타입 T 선언
    	T item;
    	
    	void setItem(T itme) { this.item = item; }
    	T getItem() { return item; }
    }
    

    클래스 옆에 ‘T’를 붙이고 ‘Object’를 전부 ‘T’로 바꾼다.

    여기서 ‘T’는 타입변수이다. 타입변수(type variable) 의 첫 글자를 따온 것이고,

    이 타입변수는 T가 아닌 다른 것을 사용해도 된다.

    무조건 ‘T’를 사용하기보다 상황에 맞게 의미있는 문자를 선택해서 사용하자.

    ex ) ArrayList<E> : Element, Map<K,V> : Key, Value

    → 즉, 기호의 종류만 다를 뿐 '임의의 참조형 타입'을 의미한다

    ex) 지네릭클래스의 객체를 생성하는 방법

    Box<String> b = new Box<String>(); //지네릭 클래스 사용시 타입 지정
    b.setItem(new Object()); //Error, String 타입이 아님
    b.setItem("ABC"); //OK
    String item = (String)b.getItem(); //형변환 필요 X
    • 참조 변수와 생성자의 대입된 타입은 일치해야한다.
    • 지네릭 클래스간의 다형성은 성립한다. ( 대입된 타입은 일치해야함 )
      • ex ) List<Product> list = new ArrayList<Product>();
    • 매개변수의 다형성도 성립한다.

    ex)

    //Product가 조상 클래스이고, Tv, Audio가 자손 클래스 일때
    ArrayList<Product> list = new ArrayList<Product>();
    list.add(new Product());
    list.add(new Tv());
    list.add(new Audio());
    
    Product p = list.get(0); //형변환 필요 X
    Tv t = (Tv) list.get(1); //타입이 다르므로 형변환 필요

     

     

    📌 지네릭 클래스의 제한

    extends로 대입할 수 있는 타입을 제한함

    class FruitBox<T extends Fruit> { //Fruit의 자손만 타입으로 지정 가능
    	..
    }
    
    FruitBox<Apple> appleBox = new FruitBox<Apple>(); // Apple클래스가 Fruit클래스의 자손
    FruitBox<Toy> toyBox = new FruitBox<Toy>(); // Error
    

     

    인터페이스의 경우에도 implements가 아닌 extends를 사용

    interface Eatable {}
    class FruitBox<T extends Eatable> { .. }
    

     

    상속과 구현을 같이 할 경우 ‘&’ 사용

    class Fruit implements Eatable {
    	public String toString() { return "Fruit"; }
    }
    
    class Apple extends Fruit { .. }
    class Toy { .. }
    
    interface Eatable {}
    
    class Box<T> {
    	..
    }
    
    class FruitBox<T extends Fruit & Eatable> extends Box<T> {}
    // 이 예제에서는 Fruit이 Eatable을 이미 구현하고 있기 때문에 다음과 같이 작성해도 상관X
    class FruitBox<T extends Fruit> extends Box<T> {}

     

    📌 지네릭 클래스의 제약

     

    1.static 멤버에 타입변수 사용 불가 → static 멤버변수는 모든 인스턴스에 공통적으로 동작

    class Box<T> {
    	static T item; //x
    	static int compare(T t1, T t2) { ... } //x
    }

     

    2.배열 생성 시 타입변수 사용 불가. 단, 타입 변수로 배열 선언은 가능

    class Box<T> {
    	T[] itemArr; //가능
    	T[] toArray() {
    		T[] tepArr = new T[itemArr.length]; //Error
    		..
    	}
    }
    

    new연산자 때문. 해당 연산자는 컴파일 시점에 타입 T가 뭔지 정확히 알아야함

     

    📌 와일드 카드

    : 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능

    ArrayList<? extends Product> list = new ArrayList<Tv>();
    ArrayList<? extends Product> list = new ArrayList<Audio>();
    
    • <? extends T> : 와일드 카드의 상한 제한, T와 그 자손들만 가능
    • <? super T> : 와일드 카드의 하한 제한, T와 그 조상들만 가능
    • <?> : 제한 없음. 모든 타입 가능. <? extends Object>와 동일한 의미

     

    메서드의 매개변수에 와일드 카드 사용 가능

    class Juicer {
    	static Juice makeJuice ( FruitBox<? extends Fruit> box ) {
    		...
    	}
    }
    
    System.out.println(Juicer.makeJuice(new FruitBox<Fruit>());
    System.out.println(Juicer.makeJuice(new FruitBox<Apple>());

     

    3) 지네릭 메서드란?

    : 메서드의 선언부에 지네릭 타입이 선언된 메서드. 선언 위치는 반환 타입 바로 앞

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

    🌟 지네릭 클래스에 정의된 타입 매개변수와 지네릭 메서드에 정의된 타입 매개변수는 다른 것

    🌟 지네릭 메서드는 지네릭 클래스가 아닌 클래스에도 정의 될 수 있음

    class FruitBox<T> { //클래스에 선언된 타입변수와
    	//메서드에 선언된 타입변수는 다름
    	static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
    }

     

    메서드를 호출할 때마다 타입을 대입해 사용 ( 대부분 생략 가능 )

    class Juicer {
    	static <T extends Fruit> Juice makeJuice(FruitBox<T> box) {
    		String tmp = "";
    		for(Fruit f : box.getList())
    			tmp += f + " ";
    			return new Juice(tmp);
    	}
    }
    
    System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); //ok
    System.out.println(Juicer.makeJuice(fruitBox)); //ok
    System.out.println(this.<Fruit>makeJuice(fruitBox)); //ok
    System.out.println(<Fruit>makeJuice(fruitBox)); // 클래스 이름 생략 불가

     

    4) 지네릭 타입의 형변환

    1.

    Box box = null;
    Box<Object> objBox = null;
    
    box = (Box) objBox; // 지네릭 -> 원시
    objBox = (Box<Object>) box; // 원시 -> 지네릭
    

    → 지네릭타입과 넌지네릭 타입간의 형변환은 경고가 발생하지만 가능함

     

    2.

    Box<Object> objBox = null;
    Box<String> strBox = null;
    
    objBox = (Box<Object>) strBox; // Error. Box<String> -> Box<Object>
    strBox = (Box<String>) objBox; // Error. Box<Object> -> Box<String>
    

    → 대입된 타입이 다른 지네릭 타입간의 형변환은 불가능

     

    3.

    Box<? extends Object> box = new Box<String>(); //Box<String> -> Box<? extends Object>
    

    → 매개변수의 다형성 적용으로 가능

    ex )

    class Fruit { public String toString() { return "Fruit"; } }
    class Apple extends Fruit { public String toString() { return "Apple"; } }
    class Grape extends Fruit { public String toString() { return "Grape"; } }
    
    static Juice makeJuice(FruitBox<? extends Fruit> box { ... }
    
    FruitBox<? extends Fruit> box1 = new FruitBox<Fruit>();
    FruitBox<? extends Fruit> box2 = new FruitBox<Apple>();
    FruitBox<? extends Fruit> box3 = new FruitBox<Grape>();
    

     

    🌟 반대로의 형변환도 성립하지만 확인되지 않은 형변환이라는 경고가 발생.

    FruitBox<? extends Fruit> box = null;
    FruitBox<Apple> appleBox = (FruitBox<Apple>) box;
    

     

    4.

    FruitBox<? extends Object> objBox = null;
    FruitBox<? extends String> strBox = null;
    
    strBox = (FruitBox<? extends String>) objBox;
    objBox = (FruitBox<? extends Object>) strBox;
    

    → 와일드 카드가 사용된 지네릭타입끼리도 형변환 가능하지만 미확정 타입 형변환 경고 발생.

     

    5) 열거형이란?

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

    C언어에서는 타입이 달라도 값이 같으면 조건식 결과가 참이었으나, Java의 열거형은 타입에 안전한 열거형이라서 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생함.

    ex)

    class EnumExample {
        private final static int MONDAY = 1;
        private final static int TUESDAY = 2;
        private final static int WEDNESDAY = 3;
        private final static int THURSDAY = 4;
        private final static int FRIDAY = 5;
        private final static int SATURDAY = 6;
        private final static int SUNDAY = 7;
        
        public static void main(String[] args) {
    
            int day = EnumExample.MONDAY;
    
            switch (day) {
                case EnumExample.MONDAY:
                    System.out.println("월요일 입니다.");
                    break;
                case EnumExample.TUESDAY:
                    System.out.println("화요일 입니다.");
                    break;
                case EnumExample.WEDNESDAY:
                    System.out.println("수요일 입니다.");
                    break;
            }
        }
    }
    

     

    Enum으로 다음과 같이 정의 가능

    enum Week { MONDAY,TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
    
    class EnumExample {
    	
    	public static void main(String[] args) {
    
            int day = Week.MONDAY;
    
            switch (day) {
                case MONDAY: //Week.Monday 로 쓰지않음.
                    System.out.println("월요일 입니다.");
                    break;
                case TUESDAY:
                    System.out.println("화요일 입니다.");
                    break;
                case WEDNESDAY:
                    System.out.println("수요일 입니다.");
                    break;
                default:
    		            System.out.println("Invalid value.");
            }
        }
        
    }
    
    • enum명은 클래스와 같이 첫 문자를 대문자로 나머지는 소문자로 정의
    • 관례적으로 열거 상수는 모두 대문자로 작성
    • 열거 상수가 여러 단어로 구성될 경우, 단어 사이를 밑줄 ‘_’ 로 연결
    • 호출 시 ‘열거형이름.상수명’으로 사용
    • 비교문 ‘==’ 사용은 가능하나 비교연산자 ‘<’, ‘>’ 등 사용 불가
    • compareTo() 사용 가능
      • 비교 대상이 같으면 0
      • 왼쪽이 크면 양수
      • 오른쪽이 크면 음수 반환

    EnumClass 메서드

    메서드 설명
    Class<E> getDeclaringClass() 열거형의 Class객체 반환
    String name() 열거형 상수의 이름을 문자열로 반환
    int ordinal() 열거형 상수가 정의된 순서를 반환 (0부터 시작)
    T valueOf(Class<T> enumType, String name) 지정된 열거형에서 name과 일치하는 열거형 상수 반환
    enum compareTo() 문자열을 입력받아서 일치하는 열거 객체를 리턴
    enum[] values() 모든 열거 객체들을 배열로 리턴

     

     

    댓글

Designed by Tistory.