래퍼 클래스
객체지향 개념에서 모든 것은 객체로 다루어져야 한다.
때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다.
래퍼 클래스들을 이용하면 기본형 값을 객체로 다룰 수 있다.
•
기본형 값들을 객체로 변환하여 작업을 수행하는 경우
◦
매개변수를 객체를 요구할 때
◦
기본형 값이 아닌 객체로 저장해야할 때
◦
객체간의 비교가 필요할 때
문자열을 숫자로 변환하기
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를 상속 받은 Apple과 Orange를 리스트에 넣을 수 있다.
그런데 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 |