14. [Java] 컬렉션 (Collection) 1 / 20230501
🧑🏻💻 TIL(Today I Learned)
🧑🏻💻 Enum, 제네릭, 예외 처리, 컬렉션 프레임워크
1. 열거형(Enum; enumerated type)
➡️ 관련 상수들을 같이 묶어 놓은 것, Java는 타입에 안전한 열거 제공
➡️ 열거형은 값과 타입을 모두 체크함
➡️ 기존의 상수들을 정의하던 방법(public static final, interface)들은 상수명이 중복되거나 타입안정성이 떨어지거나 코드가 너무 길어져 가독성이 떨어진다거나 하는 단점들이 존재함 또한 switch 문에 활용도 할 수 없음
👉🏻 이런 문제들을 효과적으로 해결하기 위해서 만들어진 것이 "enum"
📍타입 안정성이란?
: 프로그램이 의도한 대로 타입이 사용되는 것을 보장하는 것
📍switch 문은 몇 가지 제한된 데이터 타입만 사용 가능
: byte, short, char, int, enum, String, Character, Byte, Short, Integer
enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
- 이런 식으로 코드를 작성하면 단점들을 해결할 수 있고 코드를 단순하고 가독성이 좋게 만들 수 있음 또한 switch 문에서 사용 가능
- 호환되지 않는 타입 : 정수로 타입 변환을 할 수 없다고 알려줌
- Seasons는 사용자 정의 타입이기 때문에 int형으로 변환할 수 없음
- 출력 결과 : 봄
- enum을 사용하면 좀 더 간결하고 편리하게 상수 사용가능
→ 중복을 피하고 타입에 대한 안정성 보장 - switch 문에서 사용 가능
🔎 열거형의 사용
enum 열거형이름 { 상수명1, 상수명2, 상수명3 ... }
enum Seasons {
SPRING, //정수값 0 할당
SUMMER, //정수값 1 할당
FALL, //정수값 2 할당
WINTER //정수값 3 할당
}
// 관례적으로 대문자 작성
// 각각의 열거상수들은 객체
// 상수들에는 따로 값을 지정해주지 않아도 자동적으로 0부터 시작하는 정수값 할당되어 각각 상수 가리킴
🔎 사용할 수 있는 메서드
리턴 타입 | 메서드(매개변수) | 설명 |
String | name() | 열거 객체가 가지고 있는 문자열 리턴, 리턴되는 문자열은 열거타입을 정의할 때 사용한 상수 이름과 동일함 |
int | ordinal() | 열거 객체의 순번(0부터 시작)을 리턴 |
int | compareTo(비교값) | 주어진 매개값과 비교해서 순번 차이 리턴 |
열거 타입 | valueOf(String name) | 주어진 문자열의 열거 객체 리턴 |
열거 타입 | values() | 모든 열거 객체들을 배열로 리턴 |
2. 제네릭(Generic)
➡️ 타입을 구체적으로 지정한 것이 아니라 추후에 지정할 수 있도록 일반화해 두는 것
➡️ 작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해 둔 것
만약 만든 클래스와 똑같은 기능을 하면서 인스턴스 변수에 다른 타입의 데이터를 저장할 수 있게 하려면 각 타입별로 같은 내용의 클래스를 만들어야 함
👉🏻 하지만 '제네릭(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 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;
}
}
// 제네릭 사용
// 제네릭 클래스
// T 타입 매개변수 <T> 이런 식으로 클래스 이름 옆에 작성하여 클래스 내부에서
// 사용할 타입 매개변수 선언 가능 --> 임의의 타입
// 매개변수가 여러개라면 class Basket<K, V>
// T -> type K -> key V -> value R -> result
📍 제네릭을 사용하면 Basket 클래스만으로 모든 타입의 데이터를 저장할 수 있는 인스턴스 만들 수 있음
Basket<String> basket1 = new Basket<String>("바구니");
//“Basket 클래스 내의 T를 String으로 바꿔라.”
Basket<Integer> basket2 = new Basket<Integer>(1);
//“Basket 클래스 내의 T를 Integer으로 바꿔라.”
✔️ 주의할 점
- 클래스 변수에는 타입 매개변수를 사용할 수 없음
class Basket<T> {
private T item1; // O
static T item2; // X
}
// 사용하게 된다면 클래스 변수의 타입이 인스턴스 별로 달라지게 됨
// --> 클래스 변수를 통해 같은 변수를 공유하는 것이 아니게 됨
- 제네릭 클래스에서 타입 매개변수를 넣어줄 때는 기본 자료형(primitive type)이 아닌 참조 자료형(reference type)만 사용 가능
→ 제네릭 클래스에서는 '래퍼 클래스(Wrapper Class)' 사용
래퍼 클래스(Wrapper Class)란?
: 기본 타입의 데이터를 객체로 표현해야할 때 기본 타입을 객체로 다루기 위해서 사용하는 클래스
기본 자료형 | 래퍼 클래스 |
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
🔎 제한된 제네릭
➡️ 일반적으로 위에서 정리했던 것처럼 만들면 타입 제한 없이 제네릭을 만들 수 있음
➡️ 하지만 아래와 같이 타입을 지정할 수 있도록 만들어줄 수도 있음
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
// 클래스를 인스턴스화 할 때 타입으로 Flower 클래스의 하위 클래스만 가능하도록 설정
class Basket<T extends Flower> {
private T item;
...
}
class Main {
public static void main(String[] args) {
// 인스턴스화
Basket<Rose> roseBasket = new Basket<>();
Basket<RosePasta> rosePastaBasket = new Basket<>(); // 에러
}
}
------------------------------------------------------------------------
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
// 특정 인터페이스를 구현한 클래스 타입으로만 지정할 수 있도록 제한 가능
class Basket<T extends Plant> {
private T item;
...
}
class Main {
public static void main(String[] args) {
// 인스턴스화
Basket<Flower> flowerBasket = new Basket<>();
Basket<Rose> roseBasket = new Basket<>();
}
}
📍 아래와 같이 동시에 타입을 지정할 수도 있음, & 사용
→ 이러한 경우에는 클래스를 인터페이스를 앞에 위치시켜야 함
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
class Basket<T extends Flower & Plant> { // 클래스 & 인터페이스
private T item;
...
}
class Main {
public static void main(String[] args) {
// 인스턴스화
Basket<Flower> flowerBasket = new Basket<>();
Basket<Rose> roseBasket = new Basket<>();
}
}
🔎 제네릭 메서드
➡️ 클래스 전체를 제네릭으로 선언할 수도 있지만, 클래스 내부의 특정 메서드만 제네릭으로 선언할 수도 있음
class Basket {
...
public <T> void add(T element) {
...
}
}
// 타입 매개 변수 선언은 반환 타입 앞에서 이루어지며 해당 메서드 내에서만 선언한 타입 매개 변수를 사용할 수 있음
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); // 타입 지정을 생략할 수도 있음
class Basket {
...
static <T> int setPrice(T element) {
...
}
}
- 제네릭 메서드에서의 타입 매개 변수는 제네릭 클래스 타입 매개 변수와 별개의 것!
→ 지정되는 시점이 다르기 때문에
- 클래스명 앞에서 선언한 타입 매개 변수는 클래스가 인스턴스화 될 때 타입 지정
- 제네릭 메서드의 타입 지정은 메서드가 호출될 때 이루어짐
- 클래스 타입 매개 변수와는 달리 메서드 타입 매개 변수는 static 메서드에서도 선언하여 사용가능
- 제네릭 메서드는 메서드가 호출될 시점에 제네릭 타입이 결정되기 때문에 제네릭 메서드를 정의하는 시점에서 실제 어떤 타입이 입력되는지 알 수 없음
→ 그렇기 때문에 String 클래스의 메서드 length() 와 같은 메서드 사용 불가
→ 하지만 최상위 Object 클래스의 메서드는 사용가능 (equals(), toString() 등)
🔎 와일드카드
➡️ 제네릭에서 와일드카드는 어떠한 타입으로든 대체될 수 있는 타입 파라미터
➡️ 기호 ? 로 사용가능
<? extends T>
<? super T>
- <? extends T>
: 와일드카드에 상한 제한을 두는 것, T와 T를 상속받는 하위클래스 타입만 타입 파라미터로 받을 수 있도록 지정 - <? super T>
: 와일드카드에 하한 제한을 두는 것, T와 T의 상위클래스만 타입 파라미터로 받을 수 있도록 지정 - <?>
: extends와 super 키워드와 조합하지 않은 와일드카드
<? extends Object> 와 같음
모든 클래스 타입은 Object 클래스 상속을 받기 때문에 모든 클래스 타입을 타입 파라미터로 받을 수 있음
3. 예외 처리(Exception Handling)
➡️ 예기치 않게 발생하는 에러에 대응할 수 있는 코드를 미리 사전에 작성하여 프로그램의 비정상적인 종료를 방지하고 정상적인 실행 상태를 유지하기 위한 예외 처리
- 에러 발생 시점에 따라 아래와 같이 나눌 수 있음
- 컴파일 에러(Compile Error) : 컴파일할 때 발생하는 에러
→ 주로 세미콜론 생략, 오탈자, 잘못된 자료형, 잘못된 포맷 등 문법적인 문제를 가리키는 syntax 오류로부터 발생(Syntax Error)
→ 상대적으로 발견하기도 쉽고 어렵지 않게 해결가능
→ 컴파일러에 의해 발견됨 - 런타임 에러(Runtime Error) : 런타임 시에 발생하는 에러, 프로그램이 실행될 때
→ 주로 개발자가 컴퓨터가 수행할 수 없는 특정한 작업을 요청할 때 발생
→ 프로그램이 실행될 때 자바 가상 머신(JVM)에 의해 감지됨
- 컴파일 에러(Compile Error) : 컴파일할 때 발생하는 에러
🔎 에러와 예외
➡️ 에러는 한 번 발생하면 복구하기 어려운 수준의 심각한 오류
(ex. OutOfMemory(메모리 부족), StackOverFlowError 등)
➡️ 예외는 잘못된 사용 또는 코딩으로 인한 상대적으로 미약한 수준의 오류로서 코드 수정을 통해 수습가능한 오류 지칭
✔️ 예외 클래스 계층 상속도
➡️ Throwable 클래스로부터 확장, 모든 예외 최고 상위 클래스는 Exception
➡️ Exception 클래스는 일반 예외 클래스와 실행 예외 클래스로 나눌 수 있음\
- 일반 예외 클래스(Exception)
: 런타임 시 발생하는 Runtime Exception과 그 하위 클래스를 제외한 모든 Exception 클래스와 하위 클래스 가리킴
컴파일러가 코드 실행 전에 예외 처리 코드 여부를 검사한다고 하여 checked 예외라고 부르기도 함
주로 잘못된 클래스명이나 데이터 형식의 사용자 편의 실수로 발생하는 경우 많음 - 실행 예외 클래스 (Runtime Exception)
: 런타임 시 발생하는 Runtime Exception과 그 하위 클래스
컴파일러가 예외 처리 코드 여부를 검사하지 않는다는 의미에서 unchecked 예외라고 부르기도 함
개발자의 실수로 발생하는 경우가 많고 자바 문법 요소와 관련있음
클래스 간 형변환 오류, 벗어난 배열 범위, 값이 null인 참조변수 사용 등
🔎 try - catch 문
try {
// 예외가 발생할 가능성이 있는 코드를 삽입
}
catch (ExceptionType1 e1) { // 여러 종류의 예외 처리 가능 , Exception 클래스 하나로 처리도 가능 --> 하지만 권장 x
// ExceptionType1 유형의 예외 발생 시 실행할 코드
}
catch (ExceptionType2 e2) {
// ExceptionType2 유형의 예외 발생 시 실행할 코드
}
finally {
// finally 블록은 옵셔널
// 예외 발생 여부와 상관없이 항상 실행
}
// 만약 작성한 코드가 예외 없이 정상적으로 실행되면 catch 블록은 실행되지 않고 finally 블록 실행
// 만약 catch 블록이 여러 개인 경우 일치하는 하나의 catch 블록만 실행되고 예외처리 코드가 종료되거나 finally 블록으로 넘어감
// 일치하는 블록을 차지 못할 때는 예외 처리되지 못함
🔎 예외전가
➡️ try-catch 문 외에 예외를 호출한 곳으로 다시 예외를 떠넘기기
➡️ 메서드 선언부 끝에 throws 키워드와 발생할 수 있는 예외들을 쉼표로 구분하여 나열하면 됨
반환타입 메서드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ... {
...생략...
}
// 특정 메서드에서 모든 종류의 예외가 발생할 가능성이 있는 경우
void ExampleMethod() throws Exception {
}
어제 올렸어야 되는데 진도 빼고 모르는 거 다시 보고 이해하느라 시간이 없었다. 그래도 다행히 오늘은 좀 복습하고 정리해서 올릴 시간이 생겼다. 내용이 워낙 많아서 두 개 분량으로 쪼개기로 했다. 정리를 하면서 다시 보니까 또 새로운 느낌이다.😂 그래도 다시 한 번 더 챙겨보게 되고 몰랐던 부분들 이해하기 어려웠던 부분들을 짚고 가니 좀 더 수월하긴 했다. 복습만이 살 길!
'SEB_BE_45 > 공부 정리' 카테고리의 다른 글
16. [Java] Annotation, 람다식, 스트림 / 20230503 (0) | 2023.05.08 |
---|---|
15. [Java] 컬렉션(Collection) 2 / 20230502 (0) | 2023.05.03 |
13. 객체지향프로그래밍의 심화 / 20230428 (0) | 2023.04.29 |
12. 객체지향프로그래밍의 심화 / 20230427 (0) | 2023.04.27 |
11. 객체지향프로그래밍의 이해 / 20230426 (0) | 2023.04.27 |