💡 Enum (열거형)
서로 연관된 상수들의 집합 (상수 = final 키워드를 통한 변하지 않는 값 선언)
상수명은 대문자로 하는것이 관례
상수 하나하나는 객체로 간주함
따로 값을 지정해주지 않으면 0부터 시작하는 int형 값 자동할당
기본 형식
- enum 열거형이름 {상수명1, 상수명2 ...}
- enum Seasons {SPRING, SUMMER, FALL, WINTER}
- JDK1.5 이전버전에서는 Enum을 지원하지 않아 아래와 같은 전역변수를 상수로 선언하는 방식을 사용했다.
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;
위와 같이 상수를 선언하고, 상수명이 중복이 되면 컴파일 에러가 발생함
interface Seasons {
int SPRING = 1, SUMMER = 2, FALL = 3, WINTER = 4;
}
interface Frameworks {
int DJANGO = 1, SPRING = 2, NEST = 3, EXPRESS = 4;
}
1차적인 해결법으로 위와 같이 인터페이스를 사용하여 상수구분을 함으로써 해결이 가능하지만,
타입 안정성이라는 새로운 문제가 생김
이외에도, 객체 생성을 통한 방법 외 여러가지가 있지만 switch문에 활용할 수 없는 등 여러 문제가 있다.
(switch 문은 사용자정의 타입이 호환이 안됨)
Enum을 활용한 상수 정의
enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
위와 같이 enum을 활용해 코드작성을 하면 앞선 문제들의 해결과 코드의 간결을 동시에 챙길수 있다
또한, switch문에서도 활용이 가능하다
switch문을 활용한 enum의 상수 정의
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에서 사용할 수 있는 Method
enum Level {
LOW, // 0
MEDIUM, // 1
HIGH // 2
}
public class EnumTest {
public static void main(String[] args) {
Level level = Level.MEDIUM;
Level[] allLevels = Level.values();
for(Level x : allLevels) {
System.out.printf("%s=%d%n", x.name(), x.ordinal());
}
Level findLevel = Level.valueOf("LOW");
System.out.println(findLevel);
System.out.println(Level.LOW == Level.valueOf("LOW"));
switch(level) {
case LOW:
System.out.println("낮은 레벨");
break;
case MEDIUM:
System.out.println("중간 레벨");
break;
case HIGH:
System.out.println("높은 레벨");
break;
}
}
}
//출력값
LOW=0
MEDIUM=1
HIGH=2
LOW
true
중간 레벨
위의 코드에서
values() = level에 정의된 모든 상수를 배열로 반환함
name() , ordinal() = values로 받은 배열의 각각 이름과 순서를 출력값으로 반환
valueOf() = 지정된 열거형에서 이름과 일치하는 열거형의 상수를 반환
ordinal = 객체의 순번(인덱스 번호) 리턴
Java에서 열거형은 상수명의 중복,타입의 안정성, 보다 편리한 상수선언을 보장하며 switch문에서 동작가능
💡 Generic
타입을 추후에 지정할 수 있도록 일반화 해두는 것
예시
제네릭을 사용하지 않았을때 작성한 비효율적인 코드
class Basket {
private String item;
Basket(String item) {
this.item = item;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
class BasketString { private String item; ... }
class BasketInteger { private int item; ... }
class BasketChar { private char item; ... }
class BasketDouble { private double item; ... }
하지만 아래 예시로 단 하나의 Basket 클래스 만으로
모든 타입의 데이터를 저장할 수 있는 인스턴스 생성가능
class Basket<T> {
private T item;
public Basket(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
위의 Basket 클래스는 다음과 같이 인스턴스화도 가능하다
Basket<String> basket1 = new Basket<String>("기타줄");
위의 코드는 "Basket 클래스 내의 T를 String으로 바꿔라" 라는 의미이기도 하며, 실행하면 타입<T>가 전부 String이 됨
Generic Class 정의
- Generic이 사용된 클래스를 칭함
- 클래스 변수에는 타입 매개변수를 사용못함 ex) static T item1; // X
기본 형식
- class Basket<T> // 임의의 타입 매개변수 T 선언
- class Basket<T, V> // 임의의 타입매개변수 여러개 선언
Generic Class 사용
- 멤버를 구성하는 코드에 특정한 타입지정이 되지않은 클래스이므로, 제네릭을 인스턴스화를 할때 타입지정 해줘야함
- 단, 타입 매개변수에 치환될 타입으로 기본타입 지정 X , Wrapper Class O
Basket<String> basket1 = new Basket<String>("Hello");
Basket<Integer> basket2 = new Basket<Integer>(10);
Basket<Double> basket3 = new Basket<Double>(3.14);
위의 코드에서 new Bastet<> 의내용은 생략 가능(참조변수의 타입으로 유추 가능하기 때문)
Generic Class 다형성 적용
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()); // 에러
}
제한된 Generic Class
- 타입 매개변수를 선언할때 extends로 상속이 되면 상위클래스로 지정된 클래스의 하위 클래스만 지정하도록 제한
- ex) class Basket<T extends Flower>
- 특정 클래스와 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 & 를 사용하여 제한
- ex) class Basket<T extends Flower & Plant> // 무조건 클래스명을 먼저 써야함
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<>(); // 에러
Generic Method
- 클래스 전체가 아닌 클래스 내부의 특정 메소드만 제네릭 선언
- 제네릭 클래스/메소드 는 서로 다른 타입의 매개변수로 간주
- static 메소드에서도 선언/사용 가능
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); // 타입 지정을 생략할 수도 있습니다.
💡 와일드카드
- 일반적으로 extends 나 super를 조합하여 사용
와일드카드 ex)
<? extends T>
<? super T>
<? extends T>는 와일드카드에 상한 제한을 두는 것으로서,
T와 T를 상속받는 하위 클래스 타입만 타입 파라미터로 받을 수 있도록 지정
반면, <? super T>는 와일드카드에 하한 제한을 두는 것으로, T와 T의 상위 클래스만 타입 파라미터로 받도록 함
참고로, extends 및 super 키워드와 조합하지 않은 와일드카드(<?>)는 <? extends Object>와 같다
즉, 모든 클래스 타입은 Object 클래스를 상속받으므로, 모든 클래스 타입을 타입 파라미터로 받을 수 있음을 의미
class Phone {}
class IPhone extends Phone {}
class Galaxy extends Phone {}
class IPhone12Pro extends IPhone {}
class IPhoneXS extends IPhone {}
class S22 extends Galaxy {}
class ZFlip3 extends Galaxy {}
class User<T> {
public T phone;
public User(T phone) {
this.phone = phone;
}
}
- call : 휴대폰의 기본적인 통화 기능으로, 모든 휴대폰에서 사용할 수 있는 기능입니다.
- → ? extends Phone으로 타입을 제한할 수 있습니다.
- faceI : 애플의 안면 인식 보안 기능으로, 아이폰만 사용 가능합니다.
- → ? extends IPhone으로 타입을 제한할 수 있습니다.
- samsungPay : 삼성 휴대폰의 결제 기능으로, 삼성 휴대폰에서만 사용 가능합니다.
- → ? extends Galaxy로 타입을 제한할 수 있습니다.
- recordVoice : 통화 녹음 기능을 일컬으며, 아이폰을 제외한 안드로이드 휴대폰에서만 사용 가능합니다.
- → ? super Galaxy로 타입을 제한할 수 있을 것으로 보입니다.
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("안드로이드 폰에서만 통화 녹음이 가능합니다. ");
}
}
💡 Wrapper 클래스
- 프로그래밍을 하다보면 기본타입의 데이터를 객체로 표현해야 할 경우가 있는데 이때 사용함
- 자바의 모든 기본타입은 값은 갖는 객체생성이 가능하며, 이를 포장객체라고 부르며, 기본타입의 값을 내부에 두고 포장
- 래퍼 클래스로 감싸고 있는 기본타입 값은 외부에서 변경X, 값을 변경하려면 새로운 포장객체를 생성해야함
'Languages > Java' 카테고리의 다른 글
Annotation & Lambda & Stream & I/O (0) | 2022.09.15 |
---|---|
Exception & Collection Framework (2) | 2022.09.14 |
객체지향 프로그래밍 2 (다형성 & 추상화) (0) | 2022.09.07 |
심화 학습 Reference (0) | 2022.09.07 |
공부해야 할 Class & Method (0) | 2022.09.06 |