[포스코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