[Java] Wrapper 클래스 | 제네릭(generic)

Jeini·2022년 11월 28일
0

☕️  Java

목록 보기
29/70
post-thumbnail

📌 Wrapper 클래스란?

기본 자료형(primitive type)객체(object)로 감싸주는 클래스

  • 자바에서는 int, double, char 같은 기본형이 있는데, 이들은 객체가 아니고 단순한 값만 가짐.
  • 하지만 자바의 많은 기능들(컬렉션, 제네릭, OOP 개념 활용 등)은 객체를 필요로 함.
  • 그래서 자바가 기본형 ↔ 객체로 변환할 수 있도록 Wrapper 클래스를 제공함
기본형 (Primitive)Wrapper 클래스
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

📌 왜 필요할까?

1. 객체 지향적 설계

  • 자바는 “모든 게 객체다”라는 철학이 있는데, 기본형은 예외였다.
  • Wrapper를 통해 기본형을 객체처럼 다룰 수 있게 만든 것.
  • 그래서 OOP 설계나 프레임워크 제작 시 유용하다.

2. 컬렉션 사용 시

  • 자바의 ArrayList, HashMap 같은 컬렉션은 객체만 저장 가능.
  • 그래서 int 같은 기본형은 못 넣고, Integer 같은 Wrapper를 써야 함.
List<Integer> list = new ArrayList<>();
list.add(10); // int가 자동으로 Integer로 변환됨 (오토박싱)

3. 유틸리티 메서드 제공

  • Wrapper 클래스들은 유용한 메서드들을 제공한다.
int n = Integer.parseInt("123");  // 문자열 → int
String s = Integer.toString(123); // int → 문자열

4. null 가능

  • 기본형은 무조건 값이 있어야 하지만, Wrapper 클래스는 null 을 가질 수 있다.
  • DB 값 매핑할 때 유용함 (예: 값이 없으면 null 처리)

📌 오토박싱(Auto-Boxing) & 언박싱(Unboxing)

자바 5부터는 자동 변환 기능이 생겼다.

1. 오토박싱 (Auto-Boxing) → 기본형 → Wrapper 자동 변환

int a = 5;
Integer b = a;  // 오토박싱

2. 언박싱 (Unboxing) → Wrapper → 기본형 자동 변환

Integer c = 10;
int d = c;  // 언박싱

👉 덕분에 list.add(10); 이런 코드가 가능하다.


📌 제네릭(Generics)이란?

클래스나 메서드에서 사용할 타입(자료형)을 외부에서 지정할 수 있게 해주는 기능

  • 쉽게 말하면 타입에 이름표를 붙여서 재사용할 수 있는 문법이다.

👉 같은 코드인데, 들어오는 타입만 달라질 수 있을 때 제네릭을 쓰면 더 안전하고 유연해진다.


📌 제네릭을 쓰기 전 (문제점)

제네릭이 없던 시절에는 Object 로 다 받아야 했다.

List list = new ArrayList();
list.add("정잉이");
list.add(123); // int도 들어감

String name = (String) list.get(0); // 형변환 필요

👉 문제:

  • 타입이 뒤죽박죽 들어갈 수 있음 (타입 안전성 부족)
  • 값을 꺼낼 때 일일이 형변환(casting) 해야 함

📌 제네릭을 쓰면

List<String> list = new ArrayList<>();
list.add("정잉이");
// list.add(123); // 오류! (String만 가능)

String name = list.get(0); // 형변환 불필요

👉 장점:

  • 타입 안정성: 잘못된 타입을 넣으면 컴파일 단계에서 에러 발생
  • 형변환 불필요: 꺼낼 때 캐스팅 안 해도 됨

📌 제네릭 기본 문법

1. 클래스에 제네릭 적용

class Box<T> {   // T는 타입 파라미터 (Type)
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> b1 = new Box<>();
b1.set("안녕");
System.out.println(b1.get()); // 안녕

Box<Integer> b2 = new Box<>();
b2.set(100);
System.out.println(b2.get()); // 100

👉 T 자리에 String 넣으면 Box<String>, Integer 넣으면 Box<Integer> 가 된다.

2. 메서드에 제네릭 적용

public static <T> void print(T item) {
    System.out.println(item);
}

print("정잉이"); // String 타입
print(123);     // Integer 타입

3. 여러 타입 파라미터

class Pair<K, V> {
    private K key;
    private V value;
    // 생성자, getter/setter 생략
}

Pair<String, Integer> p = new Pair<>("나이", 25);

📌 제네릭의 장점

  • 타입 안정성 → 컴파일 시점에 잘못된 타입을 걸러줌
  • 형변환 불필요 → 코드 간결
  • 재사용성 → 같은 코드로 여러 타입 지원 가능

💡 제네릭 다형성


import java.util.*;

class LandAnimal { 
	public void crying() { 
    	System.out.println("육지동물"); 
    } 
}

class Cat extends LandAnimal { 
	public void crying() { 
    	System.out.println("냐옹냐옹"); 
    } 
}
class Dog extends LandAnimal { 
	public void crying() { 
    	System.out.println("멍멍"); 
    } 
}
class Sparrow { 
	public void crying() { 
    	System.out.println("짹짹"); 
    } 
}

class AnimalList<T> {
	ArrayList<T> al = new ArrayList<T>();

	void add(T animal) { 
    	al.add(animal); 
    }

    T get(int index) { 
    	return al.get(index); 
    }

    boolean remove(T animal) { 
    	return al.remove(animal); 
    }

    int size() { 
    	return al.size(); 
    }

}


public class Generic01 {
    public static void main(String[] args) {
        
        AnimalList<LandAnimal> landAnimal = new AnimalList<>(); // Java SE 7부터 생략가능함.

        landAnimal.add(new LandAnimal());
        landAnimal.add(new Cat());
        landAnimal.add(new Dog());
        // landAnimal.add(new Sparrow()); // 오류가 발생함.

        for (int i = 0; i < landAnimal.size() ; i++) {
            landAnimal.get(i).crying();
        }
    }
}
[결과값]
육지동물
냐옹냐옹
멍멍
  • 위의 예제에서 Cat과 Dog 클래스는 LandAnimal 클래스를 상속받는 자식 클래스이므로, AnimalList<LandAnimal>에 추가할 수 있다.
  • 하지만 Sparrow 클래스는 타입이 다르므로 추가할 수 없다.
profile
Fill in my own colorful colors🎨

0개의 댓글