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는 상위클래스 타입만 호출 가능