✅ 오늘의 학습 키워드
- 자바 제너릭 명시
- Queue 인터페이스와 구현 클래스
- ArrayDeque와 LinkedList
- 스택을 큐로 구현하기
🔍 오늘의 문제 분석
- Queue를 이용해서 Stack을 구현
- 두 개의 큐를 구현
오늘의 문제는 어제와 반대로 큐를 이용해서 스택을 구현하는 문제였다. 스택과 다르게 큐는 선입선출의 자료구조를 가지고 있기 때문에 어제의 논리와 다르게 구현해야 했다.
✨ 오늘의 회고
📌내가 적은 답안📌
import java.util.ArrayDeque;
import java.util.Queue;
class MyStack {
Queue<Integer> q1;
Queue<Integer> q2;
public MyStack() {
q1 = new ArrayDeque();
q2 = new ArrayDeque();
}
public void push(int x) {
while (!q1.isEmpty()) {
q2.add(q1.poll());
}
q1.add(x);
while (!q2.isEmpty()) {
q1.add(q2.poll());
}
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean empty() {
return q1.isEmpty();
}
}
처음에 pop과 top 메서드에서 Object cannot be converted to int 에러가 발생해서 당황했는데 Queue에 제네릭을 설정하지 않았기 때문이었다. 제네릭을 지정하지 않으면 Object 타입으로 간주하고 저장하기 때문에 int형을 반환해야 할 때 문제가 발생하는 것이다. 제네릭은 객체(참조형) 타입만 사용가능하기 때문에 int로 언박싱이 반드시 필요하긴 하다. 제네릭에 대해서
제네릭에 대해 내가 모르고 있던 부분이 많은 것 같아서 찾아봤다.
✍️ 제네릭은 왜 객체 타입만 가능할까?
제네릭이 객체 타입만 사용 가능한 이유는 컴파일 시점에만 존재하고 런타임에는 타입 정보가 지워지는 타입 소거라는 구조적 제약이 있기 때문이다. 타입 정보는 컴파일에만 사용되고 컴파일이 끝나면 내부적으로 타입 정보가 사라져 버린다. 타입에 대한 정보가 런타임 시점에 없어지기 때문에 오버로딩과 타입 검사가 불가능한 것이다.
public void print(Box<String> box) { ... }
public void print(Box<Integer> box) { ... }
//보기엔 오버로딩으로 가능할 것 같지만 컴파일 이후에는 둘 다 Box임. 중복 정의 에러!
if (box instanceof Box<String>) { ... } // 불가능!
if (box instanceof Box) { ... } //가능하지만 타입까지는 검사 못 함
💡컴파일된 바이트코드에서는 List<String>과 List<Integer>도 똑같은 List<Object>로만 보여서 JVM은 제네릭 타입을 알 수 없고 Object에 해당하는 박싱 클래스들만 허용 가능한 것이다!
✍️ 런타임 시에도 제네릭 타입을 유지할 수는 없을까?
Class<T>를 함께 전달하거나 TypeToken 기법과 리플렉션을 사용해서 제네릭 타입 정보를 가질 수 있다. 리플렉션은 자바 코드 자체를 객체처럼 다루는 기술이다. 이런 식으로 런타임 중에서도 제네릭 타입을 알 수 있다.
class MyClass {
List<String> myList;
}
public class Main {
public static void main(String[] args) throws Exception {
Field field = MyClass.class.getDeclaredField("myList");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypeArgs = pt.getActualTypeArguments();
for (Type typeArg : actualTypeArgs) {
System.out.println(typeArg); // class java.lang.String
}
}
}
}
TypeToken 방식은 익명 내부 클래스의 타입 정보를 리플렉션으로 꺼내는 방법이다.
abstract class TypeReference<T> {
Type type;
public TypeReference() {
Type superClass = getClass().getGenericSuperclass();
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
Type type = new TypeReference<List<String>>() {}.getType();
System.out.println(type); // java.util.List<java.lang.String>
정리하면, 컴파일 시점에는 제네릭 타입을 체크하고 오토박싱, 언박싱 코드를 생성한다. JVM이 실행 중이면 Object로만 동작하고 생성된 코드(.intValue())로만 수행한다.
'알고리즘 > [항해99] 1일 1알고리즘 스터디' 카테고리의 다른 글
99클럽 코테 스터디 7일차 TIL ✒️ 좋은 단어 (스택 활용) (0) | 2025.04.08 |
---|---|
99클럽 코테 스터디 6일차 TIL ✒️계단 오르기 (0) | 2025.04.07 |
99클럽 코테 스터디 4일차 TIL ✒️Stack을 이용해서 Queue 구현 (0) | 2025.04.03 |
99클럽 코테 스터디 3일차 TIL ✒️!!초콜릿 중독 주의!! (0) | 2025.04.03 |
99클럽 코테 스터디 2일차 TIL ✒️ ASCII 코드 변환 (0) | 2025.04.02 |