자바에서 자료구조는 배열이있었다.
하지만 index만을 이용해서 데이터 저장하는 방식은 불편했다.
이러한 단점을 극복한 것이 자바에서 제공하는 컬렉션 프레임워크이다.
List를 보기전에 먼저 제네릭 이라는것을 알아야한다.
제네릭?
클래스가 다루어야할 데이터타입을 명시하는것
외부로 부터 데이터를 받아서 명시한다.
<T> 는 Type
<E> 는 Element
<K> 는 Key
<N> 는 Number
<V> 는 Value
제네릭의 예시를 보자
일반 코드
public class DataList {
private Object[] data;
private int size;
private int defaultSize = 10;
public DataList() {
data = new Object[defaultSize];
}
public DataList(int size) {
data = new Object[size];
}
public void add(Object value) {
data[size++] = value;
}
public Object get(int index) {
return data[index];
}
public int size() {
return size;
}
}
public class DataList_Main {
public static void main(String[] args) {
DataList list = new DataList();
list.add(10);
list.add("문자");
list.add(10.33);
for(int i=0; i
Object data = list.get(i);
if(data instanceof Integer) {
System.out.println("정수 : " +(int)data);
}else if(data instanceof Double) {
System.out.println("정수 : " +(double)data);
}else if(data instanceof String) {
System.out.println("정수 : " +(String)data);
}
}
}
}
private접근제어자로 data이름으로 배열을 만들어주었는데
타입은 Object로써 최상의 클래스를 주었다.
이 의미는 어떤 타입이든 저장할수있다는 것이다.
그후 size 와 defaultSize 변수를 만들어주었다.
그리고 기본생성자와 매개변수 생성자를 만들어주었다.
main에서 생성자 생성시 매개변수 입력안하면
기본 생성자가 실행되고 입력시에는 매개변수 생성자가 실행된다.
그후 add 메서드를 만들어주었는데, 입력한값을 저장해주는것이다.
여기서 주의해서 볼것이있다.
바로 data[size++]인데 여기서 사용되는 size는
매개변수 생성자에서 받는 size와는 별개의 값이라는것이다.
매개변수의 size는 지역변수로써 괄호를 나감과 동시에
그 영향은 끝나게된다.
그후 매개변수를 입력받는 get메서드를 만들어주었다.
이 메서드는 사용자에게 정수값을 입력받아서 해당 정수에
위치한 value값을 불러온다.
여기서 또 주의해서 볼것이있다.
왜 get앞에 Object를 쓴것일까?
그 이유는 바로 데이터를 Object타입으로 반환해주기 위해서이다.
이게 무슨말일까?
Object는 최상위 클래스로 어떤 타입이던 저장할수있다고했다.
get메서드를 보면 return값으로 data[index]를 받아온다.
즉 해당위치에 저장되있는 data값을 가져오는건데
이 가져올 값의 타입은 뭐가될지 모른다.
int가될수도있고 String이 될수도있다.
왜냐하면 data 배열을 애초에 Object로 저장해놨으니까.
사용자가 어떤값이든 넣을수있으니까.
불러올때 int다 String이다 정할수가없는것이다.
그래서 Object로 지정해서 반환해준다.제네릭코드
public class DataList<T> {
private Object[] data;
private int size;
private int defaultSize = 10;
public DataList() {
data = new Object[defaultSize];
}
public DataList(int size) {
data = new Object[size];
}
public void add(T value) {
data[size++] = value;
}
public T get(int index) {
@SuppressWarnings("unchecked")
T result = (T) data[index];
return result;
}
public int size() {
return size;
}
}
public class DataList_Main {
public static void main(String[] args) {
// 타입을 명시하여 DataList 객체 생성
DataList<Object> list = new DataList<>();
list.add(10);
list.add("문자");
list.add(10.33);
for (int i = 0; i < list.size(); i++) {
Object data = list.get(i);
// 타입에 대한 안전성을 고려한 형변환과 예외 처리
if (data instanceof Integer) {
System.out.println("정수 : " + (int) data);
} else if (data instanceof Double) {
System.out.println("실수 : " + (double) data);
} else if (data instanceof String) {
System.out.println("문자열 : " + (String) data);
}
}
}
}위의 일반코드와 달라진점을 보자.
먼저 클래스이름 뒤에 <T>를 써주었다.
이는 타입을 지정해주겠다 라는것인데,
이것은 main에서 생성자를 불러올때 효과가 발생한다.
main을 보게되면,
DataList<Object>
마지막으로 이글을 적으면서 궁금했던것이 있었다.
Object 타입으로 지정해주면 어떤 타입의 데이터간에 저장이가능하다. 정말 간편하다.
그런데 제네릭을 사용하게 되면 입력받을수있는 타입이 지정되기때문에 다양한 타입의 데이터를 받지못한다.
즉 일반적으로 작성하는 코드와 다를게 없다는 생각을 하게되었다.
하지만 이유가 다있었다...
제네릭을 사용하면 타입을 명시적으로 지정할 수 있고, 컴파일러가 타입 체크를 수행하여 타입 안정성을 높일 수 있기때문이다.
'(혼) (공) (자)' 카테고리의 다른 글
컬렉션 프레임워크 - Map (0) | 2024.02.05 |
---|---|
컬렉션 프레임워크 - List (0) | 2024.02.05 |
object [ ] (1) | 2024.02.04 |
parse 메서드 (0) | 2024.02.04 |
wrapper클래스 (1) | 2024.02.04 |