Enum 과 제네릭

박대운·2022년 11월 10일
0

Java

목록 보기
4/9

Enum

열거형(enum)은 서로 연관된 상수들의 집합을 의미합니다.

상수란 변하지 않는 값을 의미하며 final 키워드를 사용하여 선언할 수 있었습니다.

정수값을 통해서 상수를 할당하면, 아래의 예시처럼 상수명이 중복되는 경우가 종종 발생할 수 있습니다.

예시)

public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int FALL   = 3;
public static final int WINTER = 4;

public static final int DJANGO  = 1;
public static final int SPRING  = 2; // 계절의 SPRING과 중복 발생!
public static final int NEST    = 3;
public static final int EXPRESS = 4;

Enum을 활용한 상수 정의

enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }

Enum을 활용한 Switch 문

enum Seasons {SPRING, SUMMER, FALL, WINTER}

public class Main {
    public static void main(String[] args) {
        Seasons seasons = Seasons.SPRING;
        switch (seasons) {
            case SPRING:
                System.out.println("봄");
                break;
            case SUMMER:
                System.out.println("여름");
                break;
            case FALL:
                System.out.println("가을");
                break;
            case WINTER:
                System.out.println("겨울");
                break;
        }
    }
}

//출력값 
봄

Enum 실제 사용

enum Seasons { SPRING, SUMMER, FALL, WINTER }

public class EnumExample {
    public static void main(String[] args) {
        Seasons favoriteSeason = Seasons.SPRING;
        System.out.println(favoriteSeason); // SPRING
    }
}


출처. codestates

제네릭(Generic)

제네릭(Generic)은 사전적으로 ‘일반적인’이라는 의미를 가지고 있습니다. 자바에서 제네릭이란, 위에서 살펴본 것처럼 클래스나 메서드의 코드를 작성할 때, 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해두는 것을 의미합니다. 즉, 작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것을 의미합니다.

제네릭 클래스 정의

예시)

Basket<Integer> basket2 = new Basket<Integer>(1);

// 위와 같이 인스턴스화하면 Basket 클래스는 아래와 같이 변환됩니다. 
class Basket<Integer> {
    private Integer item;

    public Basket(Integer item) {
        this.item = item;
    }

    public Integer getItem() {
        return item;
    }

    public void setItem(Integer item) {
        this.item = item;
    }
}

<> 안에 사용할 타입을 넣고 () 안에 값 리턴

제네릭 클래스를 정의할 때 주의할 점

예시)

class Basket<T> {
	private T item1; // O 
	static  T item2; // X 
}

위와 같이 static 변수 또는 메서드에는 사용 불가

제네릭 클래스 사용

예시)

Basket<String>  basket1 = new Basket<>("Hello");
Basket<Integer> basket2 = new Basket<>(10);
Basket<Double>  basket2 = new Basket<>(3.14);

위와 같이 new Basket<> 안에 구체적인 타입을 지정하지 않아도 사용가능.

제네릭 클래스 다형성 적용 예시)

class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }

class Basket<T> {
    private T item;

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

public static void main(String[] args) {
		Basket<Flower> flowerBasket = new Basket<>();
		flowerBasket.setItem(new Rose());      // 다형성 적용
		flowerBasket.setItem(new RosePasta()); // 에러
}

Rose 클래스가 Flower를 상속 받고 있어 Flower클래스는 상위클래스가 되고

Basket<Flower>

는 item 타입을 Flower로 지정하는 것이다.
new Rose()를 통해 생성된 인스턴스 Rose 타입이다.

제한된 제네릭 클래스

예시)

class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }

class Basket<T extends Flower> {
    private T item;
	
		...
}

public static void main(String[] args) {

		// 인스턴스화 
		Basket<Rose> roseBasket = new Basket<>();
		Basket<RosePasta> rosePastaBasket = new Basket<>(); // 에러
}
Basket<T extends Flower> 

이렇게 매개변수를 Flower를 상속받는 매개변수로 지정을 해주면 상속받는 클래스만 인스턴스화를 할 수 있습니다.

인터페이스 또한 상속 가능 예시)

interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }

class Basket<T extends Plant> {
    private T item;
	
		...
}

public static void main(String[] args) {

		// 인스턴스화 
		Basket<Flower> flowerBasket = new Basket<>();
		Basket<Rose> roseBasket = new Basket<>();
}

제네릭 메서드

클래스 전체를 제네릭으로 선언할 수도 있지만, 클래스 내부의 특정 메서드만 제네릭으로 선언할 수 있습니다. 이를 제네릭 메서드라고 합니다.

예시)

class Basket<T> {             // 1 : 여기에서 선언한 타입 매개변수 T와 ...
public <T> void add(T element) { // 2 : 여기에서 선언한 타입 매개변수 T는 서로 다른 것입니다....
		}
}

제네릭 메서드 호출 예시)

Basket<String> basket = new Bakset<>(); // 위 예제의 1의 T가 String으로 지정됩니다. 
basket.<Integer>add(10);                // 위 예제의 2의 T가 Integer로 지정됩니다. 
basket.add(10);                         // 타입 지정을 생략할 수도 있습니다. 

static 메서드 예시)

class Basket {
		...
		static <T> int setPrice(T element) {
				...
		}
}

메서드는 static도 사용 가능

Object 메서드 예시)

class Basket {
    public <T> void getPrint(T item) {
        System.out.println(item.equals("Kim coding")); // 가능
    }
}

메서드를 호출하기 전에는 Object 메서드만 호출 가능.
println(), toString(), equals() ... 등

와일드카드

자바의 제네릭에서 와일드카드는 어떠한 타입으로든 대체될 수 있는 타입 파라미터를 의미하며, 기호 ?로 와일드카드를 사용할 수 있습니다.

일반적으로 와일드카드는 extends와 super 키워드를 조합하여 사용합니다.

<? extends T>
<? super T>

? extends T 는 와일드카드에 상한 제한을 두는 것으로서, T와 T를 상속받는 하위 클래스 타입만 타입 파라미터로 받을 수 있도록 지정합니다.

반면, ? super T 는 와일드카드에 하한 제한을 두는 것으로, T와 T의 상위 클래스만 타입 파라미터로 받도록 합니다.

예시)

class PhoneFunction {
    public static void call(User<? extends Phone> user) {
        System.out.println("-----------------------------");
        System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
        System.out.println("모든 Phone은 통화를 할 수 있습니다.");
    }

    public static void faceId(User<? extends IPhone> user) {
        System.out.println("-----------------------------");
        System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
        System.out.println("IPhone만 Face ID를 사용할 수 있습니다. ");
    }

    public static void samsungPay(User<? extends Galaxy> user) {
        System.out.println("-----------------------------");
        System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
        System.out.println("Galaxy만 삼성 페이를 사용할 수 있습니다. ");
    }

    public static void recordVoice(User<? super Galaxy> user) {
        System.out.println("-----------------------------");
        System.out.println("user.phone = " + user.phone.getClass().getSimpleName());
        System.out.println("안드로이드 폰에서만 통화 녹음이 가능합니다. ");
    }
}

이제 위에서 만들어둔 휴대폰 기종 클래스들을 인자로 전달하면서 PhoneFunction의 각 메서드들을 호출해볼 것입니다.

public class Example {
    public static void main(String[] args) {
        PhoneFunction.call(new User<Phone>(new Phone()));
        PhoneFunction.call(new User<IPhone>(new IPhone()));
        PhoneFunction.call(new User<Galaxy>(new Galaxy()));
        PhoneFunction.call(new User<IPhone12Pro>(new IPhone12Pro()));
        PhoneFunction.call(new User<IPhoneXS>(new IPhoneXS()));
        PhoneFunction.call(new User<S22>(new S22()));
        PhoneFunction.call(new User<ZFlip3>(new ZFlip3()));

        System.out.println("\n######################################\n");

//        PhoneFunction.faceId(new User<Phone>(new Phone())); // X
        PhoneFunction.faceId(new User<IPhone>(new IPhone()));
        PhoneFunction.faceId(new User<IPhone12Pro>(new IPhone12Pro()));
        PhoneFunction.faceId(new User<IPhoneXS>(new IPhoneXS()));
//        PhoneFunction.faceId(new User<Galaxy>(new Galaxy())); // X
//        PhoneFunction.faceId(new User<S22>(new S22())); // X
//        PhoneFunction.faceId(new User<ZFlip3>(new ZFlip3())); // X

        System.out.println("\n######################################\n");

//        PhoneFunction.samsungPay(new User<Phone>(new Phone())); // X
//        PhoneFunction.samsungPay(new User<IPhone>(new IPhone())); // X
//        PhoneFunction.samsungPay(new User<IPhone12Pro>(new IPhone12Pro())); // X
//        PhoneFunction.samsungPay(new User<IPhoneXS>(new IPhoneXS())); // X
        PhoneFunction.samsungPay(new User<Galaxy>(new Galaxy()));
        PhoneFunction.samsungPay(new User<S22>(new S22()));
        PhoneFunction.samsungPay(new User<ZFlip3>(new ZFlip3()));

        System.out.println("\n######################################\n");

        PhoneFunction.recordVoice(new User<Phone>(new Phone()));
//        PhoneFunction.recordVoice(new User<IPhone>(new IPhone())); // X
//        PhoneFunction.recordVoice(new User<IPhone12Pro>(new IPhone12Pro())); // X
//        PhoneFunction.recordVoice(new User<IPhoneXS>(new IPhoneXS())); // X
        PhoneFunction.recordVoice(new User<Galaxy>(new Galaxy()));
//        PhoneFunction.recordVoice(new User<S22>(new S22())); // X
//        PhoneFunction.recordVoice(new User<ZFlip3>(new ZFlip3())); // X
    }
}

extends 는 하위클래스 타입만 호출 가능하고, super는 상위클래스 타입만 호출 가능

profile
성장하는사람이 되자

0개의 댓글