///////
Search

Wrapper클래스, Generics

날짜
2022/09/30
태그
자바

래퍼 클래스

객체지향 개념에서 모든 것은 객체로 다루어져야 한다. 때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다. 래퍼 클래스들을 이용하면 기본형 값을 객체로 다룰 수 있다.
기본형 값들을 객체로 변환하여 작업을 수행하는 경우
매개변수를 객체를 요구할 때
기본형 값이 아닌 객체로 저장해야할 때
객체간의 비교가 필요할 때

문자열을 숫자로 변환하기

int i1 = new Integer("100").intValue(); Integer i3 = new Integer("100"); //많이 사용하는 방법 int i2 = Integer.parseInt("100"); Integer i4 = Integer.valueOf("100");
Plain Text
복사

Generics(지네릭스)

정의

클래스 내부에서 사용할 데이터 타입을 외부에서 지정해주는 기법

지네릭 클래스 선언

// 일반적인 클래스 class Box { Object item; void setItem(Object item) {this.item = item;} Object getItem() {return item;} } // 제네릭 클래스 class Box<T> { T item; void setItem(T item) {this.item = item;} T getItem() {return item;} }
Plain Text
복사
여기에서 T는 임의의 변수이고 일종의 매개변수 역할과도 비슷하다. T 자리에는 어떠한 참조형 타입이 들어간다는 뜻이다.

지네릭스 사용 전

예제에서 사용할 클래스

class Apple { @Override public String toString() { return "Apple"; } } class Orange { @Override public String toString() { return "Orange"; } } class Box { private Object ob; public void set(Object o) { ob = o; } public Object get() { return ob; } }
Plain Text
복사

main()

원래 의도대로 잘 작성한 코드

public class GenericsEx { public static void main(String[] args) { Box aBox = new Box(); Box oBox = new Box(); aBox.set(new Apple()); oBox.set(new Orange()); Apple ap = (Apple) aBox.get(); Orange og = (Orange) oBox.get(); System.out.println(ap); System.out.println(og); } }
Plain Text
복사

문제1 - 런타임에러

set() 메서드는 파라미터 타입이 Object이므로 String도 받을 수 있다.
협업하는 과정에서 의도와 다르게 메서드를 호출해서 사용한 결과 런타임 에러가 발생할 수 있다는 상황을 가정해 아래와 같이 코드를 작성 해본다.
public class GenericsEx { public static void main(String[] args) { Box aBox = new Box(); Box oBox = new Box(); // 개발자의 실수 aBox.set("Apple"); oBox.set("Orange"); Apple ap = (Apple) aBox.get(); Orange og = (Orange) oBox.get(); System.out.println(ap); System.out.println(og); } }
Plain Text
복사
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class date220930.Apple (java.lang.String is in module java.base of loader 'bootstrap'; date220930.Apple is in unnamed module of loader 'app') at date220930.GenericsEx.main(GenericsEx.java:35)
Plain Text
복사
런타임 에러야 어찌되었든 에러라도 뜨니까 어디에서 문제가 일어났는지 알아차릴 수 있다. 더 큰 문제는 다음 상황을 가정해보자.

문제2 - 의도와 다르게 실행

런타임 에러조차 뜨지 않고 정상적으로 코드가 실행은 되나 의도대로 전혀 프로그램이 작동하지 않을 수 있다.
public class GenericsEx { public static void main(String[] args) { Box aBox = new Box(); Box oBox = new Box(); // 개발자의 실수 aBox.set("Apple"); oBox.set("Orange"); System.out.println(aBox.get()); System.out.println(oBox.get()); } }
Plain Text
복사
Apple Orange
Plain Text
복사
두 가지의 문제점으로 인해 Generics라는 새로운 문법을 1.5 버전 이상부터 도입하여 문제를 해결하는 방법이 있다.

지네릭스 사용 후

지네릭스 적용 클래스 정의

class Box<T> { private T ob; public void set(T o) { ob = o; } public Object get() { return ob; } }
Plain Text
복사

main()

지네릭스를 도입한 결과

public class GenericsEx { public static void main(String[] args) { Box<Apple> aBox = new Box<Apple>(); Box<Orange> oBox = new Box<Orange>(); Apple ap = aBox.get(); // 타입 변환이 불필요. Orange og = oBox.get(); // 타입 변환이 불필요. System.out.println(ap); System.out.println(og); } }
Plain Text
복사

문제 상황 재현

// 개발자의 실수 aBox.set("Apple"); oBox.set("Orange");
Plain Text
복사
set() 메서드는 Apple 타입 객체만 받을 수 있으므로 컴파일 에러를 발생시킨다.

지네릭스의 장점

1.
타입 안정성을 제공한다.
2.
타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 진다.
3.
런타임 에러를 컴파일 에러로 끌어올 수 있다.

제한된 지네릭 클래스

왜 필요할까?

타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장할 수 있도록 제한할 수 있지만, 그래도 여전히 모든 종류의 타입을 지정할 수 있다는 것에는 변함이 없다.
import java.util.ArrayList; class Fruit { } class Apple extends Fruit{ } class Orange extends Fruit{ } class Animal { } class Cat extends Animal { } class Dog extends Animal { } class FruitBox<F> { ArrayList<F> list = new ArrayList<F>(); void add(F item) { list.add(item); } } public class GenericsEx { public static void main(String[] args) { FruitBox<Fruit> fruitBox= new FruitBox<Fruit>(); fruitBox.add(new Apple()); fruitBox.add(new Orange()); // fruitBox.add(new Cat()); //컴파일 에러 // fruitBox.add(new Dog()); //컴파일 에러 FruitBox<Animal> animalBox = new FruitBox<>(); } }
Plain Text
복사
다음과 같이 fruitBox 객체에는 제네릭을 활용하였기 때문에 다형성을 이용해서 Fruit를 상속 받은 AppleOrange를 리스트에 넣을 수 있다.
그런데 FruitBox클래스를 이용해서 animalBox를 만들어도 아무런 오류는 발생하지 않는다.
만약에 FruitBox클래스에서 Fruit에만 있는 메서드를 호출하는 행위를 하게 된다면 오류가 나게 되므로 제한된 제네릭 클래스가 필요하다.

제한된 제네릭 클래스 적용

//---생략 class FruitBox<F extends Fruit> { ArrayList<F> list = new ArrayList<F>(); void add(F item) { list.add(item); } } public class GenericsEx { public static void main(String[] args) { FruitBox<Fruit> fruitBox= new FruitBox<Fruit>(); fruitBox.add(new Apple()); fruitBox.add(new Orange()); // fruitBox.add(new Cat()); //컴파일 에러 // fruitBox.add(new Dog()); //컴파일 에러 // FruitBox<Animal> animalBox = new FruitBox<>(); //컴파일 에러 } }
Plain Text
복사
적용한 결과 FruitBox 인스턴스를 생성할 때는 타입변수에는 Fruit를 상속받은 타입만 올 수 있다. 만약 Fruit가 클래스가 아닌 인터페이스여도 implements가 아니라 extends이다. 두 개 이상을 상속받아야 한다면 class FruitBox<F extends Fruit & Food> {...}와 같이 & 연산자를 이용하면 된다.
기본형
래퍼클래스
생성자
활용예
boolean
Boolean
Boolean(boolean value) </br> Boolean(String s)
Boolean b1 = new Boolean(true); </br> Boolean b2 = new Boolean("true");
char
Character
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double