728x90
Interface
인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 즉, 인터페이스는 해당 클래스를 사용하는 클라이언트가 클래스의 구현이 아닌, 정의된 메서드를 통해서만 객체와 상호작용할 수 있게 만든다.
클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에게 알려주는 것이다. 인터페이스는 기능을 정의하는 것이 아니라 타입을 정의하는 용도로만 사용해야 한다. 즉, 인터페이스는 구현이 아닌 계약(contract)을 정의하는데 사용된다.
상수 인터페이스
상수 인터페이스는 메서드 없이, 오직 static final
필드만 포함된 인터페이스를 말한다. 이는 잘못된 사용 사례에 해당하며, Java에서는 상수를 정의할 때 인터페이스를 사용하는 것을 피해야 한다. 이러한 방식은 불필요한 의존성을 유발하고, 바이너리 호환성 문제를 발생시킬 수 있다.
잘못된 예시: 상수 인터페이스
// 상수 인터페이스 안티패턴 - 인터페이스를 잘못 사용한 예
public interface PhysicalConstants {
// 아보가드로 수 (1/몰)
static final double AVOGADROS_NUMBER = 6.022_140_857e23;
// 볼츠만 상수 (J/K)
static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
// 전자 질량 (kg)
static final double ELECTRON_MASS = 9.109_383_56e-31;
}
문제점
- 내부 구현에 대한 의존성:
PhysicalConstants
인터페이스를 구현한 클래스는 해당 상수들에 강하게 의존하게 되어, 내부 구현이 변경되었을 때 사용자 코드에 영향을 미칠 수 있다. - 바이너리 호환성 문제: 상수 인터페이스를 구현하는 클래스가 삭제되거나 변경되어도 기존 바이너리와 호환성을 유지해야 할 필요가 생기는데, 이는 불필요한 복잡성을 초래한다.
올바른 사용 방법
클래스가 정의하는 상수는 해당 클래스나 관련된 다른 클래스를 통해 제공해야 한다. 예를 들어, Shape
클래스 내부에 도형과 관련된 상수를 정의하는 것이 더 적절하다.
// 올바른 예시: 특정 클래스에 관련된 상수 정의
public class Shape {
// 상수로 도형의 타입을 정의합니다.
public static final int CIRCLE = 1;
public static final int RECTANGLE = 2;
public static final int TRIANGLE = 3;
// Shape 관련 메서드들
public static String getShapeType(int shapeType) {
switch (shapeType) {
case CIRCLE: return "Circle";
case RECTANGLE: return "Rectangle";
case TRIANGLE: return "Triangle";
default: return "Unknown Shape";
}
}
}
이렇게 하면 상수는 Shape
클래스 내에서만 사용되며, 클라이언트 코드에서 외부 클래스를 의존하지 않고 Shape
클래스만 참조하게 된다.
예시
public class TestShape {
public static void main(String[] args) {
int shapeType = Shape.CIRCLE;
System.out.println("Shape: " + Shape.getShapeType(shapeType));
}
}
인터페이스의 올바른 사용 사례
- 타입 정의: 인터페이스는 특정 클래스를 사용하는 데 있어 필요한 메서드를 정의하는 계약을 나타낸다. 이를 통해 클래스의 구체적인 구현에 의존하지 않고, 해당 클래스의 기능만을 사용할 수 있다.
Animal
인터페이스는sound
라는 메서드를 정의만 하고, 이를 구현하는Dog
,Cat
클래스는 각기 다른 방식으로 이를 구현한다. public interface Animal { void sound(); } public class Dog implements Animal { @Override public void sound() { System.out.println("Woof!"); } } public class Cat implements Animal { @Override public void sound() { System.out.println("Meow!"); } }
- 다형성 지원: 인터페이스는 다형성을 지원한다. 즉,
Animal
타입의 변수로Dog
나Cat
객체를 참조할 수 있기 때문에 코드의 유연성과 재사용성이 높아진다. public class TestAnimal { public static void main(String[] args) { Animal animal1 = new Dog(); Animal animal2 = new Cat(); animal1.sound(); // "Woof!" animal2.sound(); // "Meow!" } }
728x90
'Programming Language > Java' 카테고리의 다른 글
[이펙티브 자바]Item.24-멤버 클래스는 되도록 static으로 만들어라 (1) | 2025.01.16 |
---|---|
[이펙티브 자바]Item.23-태그달린 클래스보다는 클래스 계층구조를 활용하라 (0) | 2025.01.16 |
[이펙티브 자바]Item.20-추상 클래스보다는 인터페이스를 우선하라 (1) | 2024.12.11 |
[이펙티브 자바]Item.18-상속보다는 컴포지션을 사용하라 (1) | 2024.12.11 |
[이펙티브 자바]Item.17-변경 가능성을 최소화하라 (0) | 2024.12.02 |