[포스코x코딩온] 웹개발자 풀스택 부트캠프8기
[포스코x코딩온] Java (Wrapper 클래스, 오토박싱과 오토언박싱, 제네릭, 와일드 카드, 컬렉션)
항상 발전하는 개발자
2023. 10. 26. 20:56
728x90
Wrapper 클래스
- 8가지 기본 데이터 타입(primitive data type)을 객체로 표현하기 위해 제공되는 클래스
- 객체로 다양한 메소드와 속성을 사용
byte-Byte | float-Float | short-Short | double-Double |
int-Integer | char-Character | long-Long | boolean-Boolean |
- 컬렉션 저장 : 자바의 컬렉션(ex, ArrayList)은 기본 데이터 타입을 직접 저장할 수 없다. 기본 데이터 타입을 저장하고 싶을 때 Wrapper 클래스를 사용한다.
- Null 값 허용 : 기본 데이터 타입은 null 값을 가질 수 없다. 그런데, 어떤 값이 없거나 알 수 없는 경우를 표현하고 싶을 때, Wrapper 클래스는 null 값을 가질 수 있어 유용하다.
- 메소드와 유틸리티 : Wrapper 클래스는 문자열 반환, 값 비교와 같은 유용한 메소드들을 제공한다.
- 메소드의 매개변수 : 메소드에 객체를 매개변수로 전달하거나 반환해야 할 때 Wrapper 클래스가 유용하다.
오토박싱과 오토언박싱
- auto-boxing : 기본 데이터 타입을 Wrapper 클래스 객체로 자동 변환
- auto-unboxing : Wrapper 클래스 객체를 기본 데이터 타입으로 자동 변환
ArrayList<Integer> numbers = new ArrayList<>();
number.add(1); //오토박싱을로 int가 Integer로 변환되어 저징
number.add(2);
int sum = numbers.get(0) + numbers.get(1);
//오토언박싱으로 Integer가 int로 변환
System.out.println("합계: " + sum);
제네릭
- 제네릭은 자바에서 형 안전성(type safety)을 높이기 위해 도입된 프로그래밍
- 제네릭을 사용하면, 컴파일 시간에 타입 오류를 더욱 효과적으로 찾아낼 수 있으며 클래스, 인터페이스, 메소드에 대한 타입을 파라미터로 전달할 수 있게 해 준다.
장점
- 타입 안전성 : 잘못된 타입의 객체가 저장되는 것을 컴파일 시간에 방지
- 형 변환 필요성 감소 : 제네릭을 사용하면 명시적인 형 변환이 필요 없어짐
- 코드 재사용성 : 일반 클래스나 메소드로 다양한 타입에 대해 동작하는 코드를 작성할 수 있다.
package Java_Generic;
import java.util.ArrayList;
//일반 클래스
class MyCustomList{
ArrayList<String> list = new ArrayList<>();
public void addElement(String element) {
list.add(element);
}
public void removeElement(String element) {
list.remove(element);
}
}
//제네릭 클래스
//코드의 중복을 막고 코드 재사용성을 높이기 위해
//만약, MyCustomList 클래스에 String 말고 int, double 등을 넣을려면 같은 코드를 여러 개 반복해서 코드를 작성해야 한다.
//이를 해결하기 위해 제네릭 클래스를 사용.
//T를 사용함으로서 Main에서 부를 때 타입을 지정해서 사용할 수 있다.
class MyCustomListGeneric<T>{
ArrayList<T> list = new ArrayList<>();
public void addElement(T element) {
list.add(element);
}
public void removeElement(T element) {
list.remove(element);
}
public T get(int index) {
return list.get(index);
}
}
public class Generic {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCustomList list = new MyCustomList();
list.addElement("element1");
list.addElement("element2");
// list.addElement(1);
// list.addElement(0.4);
MyCustomListGeneric <String> list2 = new MyCustomListGeneric<>();
list2.addElement("element1");
list2.addElement("element2");
list2.addElement("element3");
String text = list2.get(1);
System.out.println(text);
MyCustomListGeneric<Integer> list3 = new MyCustomListGeneric<>();
list3.addElement(3);
list3.addElement(2);
list3.addElement(6);
Integer number = list3.get(2);
System.out.println(number);
}
}
package Java_Generic;
//제네릭 클래스
class Box<T>{
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class GenericExample {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello World");
String value = stringBox.getItem();
System.out.println(value);
}
}
제네릭 제한
- extends 키워드를 제네릭에서 사용하면, 해당 타입 파라미터에 대한 상한을 지정할 수 있다.
- 이를 통해 타입 파라미터가 특정 클래스의 서브 클래스, 또는 특정 인터페이스의 구현 클래스만 가능하도록 제한할 수 있다.
- 사용법
- <T extends 제한 타입>
package Java_Generic;
//제네릭 클래스
//extends는 제한으로 Number에 해당하는 타입만 넣을 수 있다.
class Box<T extends Number>{
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
interface Movable{
void move();
}
class Car implements Movable{
@Override
public void move() {
System.out.println("자동차 출발");
}
}
//제네릭 클래스
//Car을 사용하기 위해 Movable로 제한을 둔다.
//(Container 클래스는 Movable인터페이스를 구현하는 클래스만 허용)
class Container<T extends Movable>{
private T item;
public Container(T item) {
this.item = item;
}
public void maekItMove() {
item.move();
}
}
public class GenericExample {
public static void main(String[] args) {
// Box<String> stringBox = new Box<>();
// stringBox.setItem("Hello World");
// String value = stringBox.getItem();
// System.out.println(value);
Box<Integer> intBox = new Box<>();
intBox.setItem(5);
Box<Double> doubleBox = new Box<>();
doubleBox.setItem(4.4);
Container<Car> carContainer = new Container<>(new Car());
carContainer.maekItMove();
}
}
와일드 카드
- 제네릭에서 와일드카드("?")는 "알 수 없는 타입"을 의미
- 와일드카드는 제네릭 코드에서 더 큰 유연성을 얻기 위해 사용되며 특히 제네릭 메소드나 제네릭 클래스에서 다양한 제네릭 타입을 처리할 때 유용하게 사용된다.
- ? (Unbounded Wildcard) : 어떠한 타입도 될 수 있다.
- ? extends T (Upper Bounded Wildcard) : T타입 또는 T의 서브타입을 의미 ( ~을 상속하는 클래스, 인터페이스...)(상위제한)
- ? super T (Lower Bounded Wildcard) : T타입 또는 T의 슈퍼타입을 의미(~이 상속하는 클래스, 인터페이스...)(하위 제한)
package Java_Generic;
import java.util.ArrayList;
//extends : ~을 상속하는 클래스, 인터페이스...
//super : ~이 상속하는 클래스, 인터페이스...
public class GenericExample2 {
//와일드 카드
public void processList(ArrayList<? extends Number> list) {
for(Number num: list) {
System.out.println(num);
}
//불가능
// list.add(1);
}
//타입파라미터
public <T extends Number> void addToNumberList(ArrayList<T> list, T item) {
list.add(item);
}
// ? super T
public void addNumber(ArrayList<? super Integer> list) {
for(int i=1; i <=5; i++) {
list.add(i);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
GenericExample2 example = new GenericExample2();
ArrayList<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
ArrayList<Double> doubleList = new ArrayList<>();
doubleList.add(3.3);
doubleList.add(2.2);
doubleList.add(1.1);
//와일드카드를 사용하여 리스트의 내용 출력
example.processList(integerList);
example.processList(doubleList);
//타입 파라미터를 사용하여 리스트에 아이템 추가
example.addToNumberList(integerList, 4);
example.addToNumberList(doubleList, 4.4);
System.out.println("--------------------------");
example.processList(integerList);
example.processList(doubleList);
ArrayList<Number> numbers = new ArrayList<>();
//Number을 보냈지만 super를 통해 하위 제한이 Integer만 올 수 있다.
example.addNumber(numbers);
System.out.println(numbers);
}
}
컬렉션(Collection)
- 데이터 구조와 알고리즘을 제공하는 프레임워크, 객체의 그룹을 효율적으로 관리하기 위한 다양한 클래스와 인터페이스를 제공
- 컬렉션 프레임워크의 주요 인터페이스
- Collection : 가장 기본적인 인터페이스로, 모든 컬렉션 클래스가 이를 구현
- List : 순서가 있는 데이터의 집합을 다룰 때 사용. 데이터 중복을 허용
- Set : 순서가 없는 데이터의 집합을 다룰 때 사용. 데이터 중복을 허용하지 않음
- Map : 키와 값의 쌍으로 데이터를 저장. 키는 중복될 수 없다.
List
- List 순서가 있는 컬렉션. 이는 요소의 순서가 유지되며 중복된 요소를 허용한다.
- 주요 메소드
- add(E e) : 요소를 리스트의 끝에 추가
- add(int index, E element) : 지정된 위치에 요소를 삽입
- get(int index) : 지정된 위치의 요소를 반환
- remove(int index) : 지정된 위치의 요소를 삭제하고 반환
- set(int index, E element) : 지정된 위치의 요소를 지정된 요소로 교체
- indexOf(Object o) : 지정된 요소의 인덱스를 반환(존재하지 않을 경우 -1)
- size() : 리스트의 크기를 반환
- ArrayList
- ArrayList는 List 인터페이스의 동적 배열 구현
- 초기 크기가 있지만, 요소가 추가됨에 따라 자동으로 크기가 확장됨
- 배열 기반이므로 인덱스를 사용한 요소 접근이 빠르지만, 중간에 요소를 삽입하거나 삭제하는 연산은 느리다.
- LinkedList
- LinkedList는 List와 Deque 인터페이스의 양방향 연결 리스트 구현
- 연결 리스트 기반이므로 중간에 요소를 삽입하거나 삭제하는 연산이 빠르지만, 인덱스를 사용한 요소 접근은 느린 편이다.
Set
- Set은 중복된 요소를 저장할 수 없으며 순서를 보장하지 않는다.
- 주요 메소드
- add(E e) : 요소를 Set에 추가한다. 요소가 이미 존재할 경우 추가되지 않는다.
- remove(Object o) : 지정된 요소를 Set에서 제거
- contains(Object o) : Set이 지정된 요소를 포함하고 있는지 확인
- size() : Set의 크기(요소의 수)를 반환
- isEmpty() : Set이 비어있는지 확인
- clear() : Set의 모든 요소를 제거
- HashSet
- Set의 대표적인 클래스로, 해시 테이블을 사용하여 요소를 저장
- 순서를 보장하지 않으며, 중복된 값을 저장할 수 없다.
- LinkedHashSet
- LinkedHashSet은 HashSet을 확장하여 요소의 삽입 순서를 기억
- 이로 인해 요소의 삽입 순서대로 반복
- TreeSet
- 자동으로 정렬된 순서로 요소를 저장하며, 사용자 정의 정렬도 가능
Map
- Map은 키와 값을 쌍으로 저장하는 데이터 구조. 각 키는 고유해야 한다.
- 주요 메소드
- put(K key, V value) : 지정된 키와 값을 맵에 저장
- get(Object key) : 지정된 키에 연관된 값을 반환
- remove(Object key) : 지정된 키와 그에 연관된 값을 맵에서 제거
- containsKey(Object key) : 맵이 지정된 키를 포함하고 있는지 확인
- containsValue(Object value) : 맵이 지정된 값을 포함하고 있는지 확인
- size() : 맵에 저장된 키-값 쌍의 수를 반환
- clear() : 맵의 모든 키-값 쌍을 제거
- HashMap
- 해시 테이블을 사용하여 키-값 쌍을 저장
- 순서를 보장하지 않음
- LinkedHashMap
- LinkedHashMap은 HashMap을 확장하여 키-값 쌍의 삽입 순서나 접근 순서를 기억
- 순서가 중요한 경우에 유용
- TreeMap
- 키에 따라 자동으로 정렬됨
package Java_Generic;
import java.util.*;
public class Collection {
public static void main(String[] args) {
// TODO Auto-generated method stub
//LikedList
List<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
//인덱스번호1에 C 넣기
linkedList.add(1, "C");
System.out.println("linkedList: " + linkedList);
//HashSet
//중복을 제거
Set<String> hashSet = new HashSet<>();
hashSet.add("A");
hashSet.add("B");
hashSet.add("A");
System.out.println("hashSet: " + hashSet);
//LinkedHashSet
//삽입 순서 기억
//삽입 순서대로 반복
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("C");
linkedHashSet.add("B");
linkedHashSet.add("A");
System.out.println("linkedHashSet: " +linkedHashSet);
//TreeSet
//자동으로 정렬
Set<String> treeSet = new TreeSet<>();
treeSet.add("B");
treeSet.add("C");
treeSet.add("A");
System.out.println("treeSet: "+ treeSet);
//HashMap
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("One", 1);
hashMap.put("Two", 2);
hashMap.put("Three", 3);
System.out.println("hashMap: "+ hashMap);
//LinkedHashMap
Map<String, Integer> list = new LinkedHashMap<>();
list.put("three", 3);
list.put("one", 1);
list.put("two", 2);
System.out.println("LinkedHashMap: "+list);
//TreeMap
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("C", 3);
treeMap.put("A", 1);
treeMap.put("B", 2);
System.out.println("treeMap: "+treeMap);
}
}
728x90