인터페이스는 클래스와 유사하지만, 오직 '무엇을 해야 하는지'만을 정의한 완벽한 설계도입니다.
인터페이스 내에서는 많은 것들이 자동으로 적용되어 생략이 가능합니다.
public static final이 붙어 상수가 됩니다.public abstract가 붙어 추상 메서드가 됩니다. (Java 8 이전 기준)public interface Shape {
// 자동으로 public static final double PI = 3.14;
double PI = 3.14;
// 자동으로 public abstract void draw();
void draw();
}
기존에는 인터페이스에 구현부를 가질 수 없었으나, 유지보수와 유틸리티 기능을 위해 메서드 구현이 가능해졌습니다.
인터페이스를 구현(implements)할 때 가장 많이 하는 실수는 접근 제한자를 좁히는 것입니다.
Rule: 오버라이딩 시 조상(인터페이스)보다 접근 권한을 좁게 설정할 수 없습니다. 인터페이스 메서드는 기본이
public이므로, 구현 클래스에서도 반드시public을 명시해야 합니다.
제네릭은 데이터 타입을 컴파일 시점에 결정하여 런타임 에러를 방지하고 불필요한 타입 캐스팅을 제거합니다.
제네릭 객체를 생성할 때는 좌변과 우변의 타입이 반드시 일치해야 합니다. 다형성이 적용되지 않는 지점임을 유의해야 합니다.
// 올바른 예시 (Java 7+ 다이아몬드 연산자)
Box<String> box = new Box<>();
// 잘못된 예시: 상속 관계여도 타입은 일치해야 함
// Box<Parent> box = new Box<Child>(); (X) -> 컴파일 에러 발생
static 멤버에는 타입 파라미터 T를 사용할 수 없습니다.
static은 인스턴스 생성 전 메모리에 올라가지만, T는 인스턴스가 생성될 때 결정되기 때문입니다.static 선언이 가능합니다.public class Box<T> {
private T item; // 가능
// public static T staticItem; // 불가능!
// 제네릭 메서드는 static 가능 (클래스의 T와는 별개임)
public static <E> void printItem(E item) {
System.out.println(item);
}
}
공부를 하며 가장 혼동되었던 두 개념을 비교 정리합니다.
| 구분 | 추상 클래스 (Abstract Class) | 인터페이스 (Interface) |
|---|---|---|
| 핵심 키워드 | extends (상속) | implements (구현) |
| 관계 | is-a (~은 ~이다) | can-do (~을 할 수 있다) |
| 다중 상속 | 불가능 (단일 상속) | 가능 (다중 구현) |
| 필드 | 일반 변수 가질 수 있음 | 상수(public static final)만 가능 |
| 목적 | 조상의 특징을 물려받아 확장 | 클래스 간의 결합도를 낮추고 규격 맞춤 |
고정 크기 배열을 순회할 때, array.length를 기준으로 돌리면 데이터가 없는 칸에서 null 참조 오류가 발생합니다. 반드시 현재 저장된 개수(size)를 기준으로 순회해야 합니다.
배열 중간의 요소를 삭제할 때, 뒤의 데이터를 한 칸씩 당기는 대신 가장 마지막 요소를 삭제된 위치로 옮기는 방식을 사용하면 의 속도로 처리가 가능합니다. (단, 순서 유지가 중요하지 않을 때 한함)
// 마지막 요소를 삭제 위치로 복사 후 개수 감소
items[i] = items[--size];
items[size] = null; // GC를 위해 명시적 null 처리