추상 클래스와 인터페이스는 비슷한 듯 다르다. 둘의 문법적 특징, 어떤 상황에서 어떤 것을 선택해야 하는지 등을 정리해보고자 한다.
추상 클래스에 대한 예시 코드는 다음과 같다.
public abstract class Shape {
// 추상 메서드
public abstract double area();
public abstract double perimeter();
// 일반 메서드
public void displayAreaAndPerimeter() {
double shapeArea = area();
double shapePerimeter = perimeter();
System.out.println("Area: " + shapeArea);
System.out.println("Perimeter: " + shapePerimeter);
}
}
앞서 추상 클래스는 인스턴스 생성이 불가하다고 적었으나, 다음과 같이 생성하는 방법이 있긴 하다.
Shape shape = new Shape() {
@Override
public double area() {
return 1.5;
}
@Override
public double perimeter() {
return 0.5;
}
};
new 키워드로 인스턴스를 생성하면서 동시에 추상 메서드에 대한 구현을 넣어주는 것이다. 이것은 추상 메서드가 즉석에서 오버라이딩된 것과 같은 효과를 가진다. (찾아보니 이런 식의 구현을 익명 내부 클래스라고 한다고 한다. 추후 내용 정리해볼 것!)
인터페이스에 대한 예시 코드는 다음과 같다.
public interface Shape {
double area();
double perimeter();
default void displayArea() {
this.area();
}
}
자바 인터페이스에서 default 메서드가 도입된 후에는 인터페이스와 추상 클래스 간에 더 이상 차이가 없는 것처럼 보이기도 한다. 그러나 둘 사이에는 몇 가지 차이점이 존재한다.
다음과 같이 CircleClass라는 추상 클래스를 만들었다고 가정해보자. 이 클래스에는 CircleClass 객체의 상태를 나타내는 문자열 color가 포함되어있다.
public abstract class CircleClass {
private String color;
private List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");
public boolean isValid() {
if (allowedColors.contains(getColor())) {
return true;
} else {
return false;
}
}
// 표준 getter 및 setter
}
위의 추상 클래스에서는 color를 기반으로 CircleClass 객체의 유효성을 검사하는 isValid() 메서드와 같은 일반 메서드가 있다. isValid() 메서드는 CircleClass 객체의 인스턴스 변수에 접근하고, 허용된 색상을 기반으로 CircleClass 인스턴스의 유효성을 검사할 수 있다.
이렇게 추상 클래스 메서드에서는 객체의 상태를 기반으로 어떤 논리든 작성할 수 있다.
다음은 기본 메서드를 갖는 인터페이스를 사용하여 비슷한 작업을 수행한 코드이다.
public interface CircleInterface {
List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");
String getColor();
public default boolean isValid() {
if (allowedColors.contains(getColor())) {
return true;
} else {
return false;
}
}
}
인터페이스 자체는 인스턴스 변수를 가질 수 없다. 따라서 구현 객체의 상태 정보를 제공하기 위해 getColor() 메서드를 정의하고, 다음과 같이 구현 클래스가 getColor() 메서드를 오버라이딩하여 런타임에 인스턴스의 상태를 제공한다.
public class ChildCircleInterfaceImpl implements CircleInterface {
private String color;
@Override
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
앞서 추상 클래스의 문법적 특징에서도 언급하였듯이, 추상 클래스는 new 키워드로 인스턴스를 생성할 수 있다.
자바의 Object 클래스는 모든 클래스의 부모 클래스이며, 다른 클래스에서 오버라이딩이 가능한 메서드를 가지고 있다. 자주 오버라이딩 되는 메서드에는 toString(), equals(), hashCode() 등이 있다.
추상 클래스는 다음과 같이 Object 클래스의 메서드를 오버라이딩 할 수 있다.
public abstract class MyAbstractClass {
@Override
public String toString() {
// 객체의 문자열 표현을 사용자 지정 방식으로 반환
return "This is a custom string representation of the object.";
}
@Override
public boolean equals(Object obj) {
// 객체 간의 동등성을 사용자 지정 조건에 따라 판단
// 사용자가 정의한 비교 로직을 여기에 구현
}
}
추상 클래스는 모든 가능한 접근 제어자를 가진 인스턴스 변수를 선언할 수 있으며, 하위 클래스에서 액세스할 수 있다.
추상 클래스는 인스턴스 및 정적 블록을 선언할 수 있지만, 인터페이스는 선언할 수 없다.
정적 블록(Static Block)은 클래스를 초기화하는 블록이다. 특정 클래스가 로드될 때 실행되는 블록으로, 해당 클래스에 대한 정적 초기화를 수행할 수 있다.
정적 블록은 클래스가 로드될 때 한 번만 실행되며, 그 이후로는 다시 실행되지 않는다. 주로 클래스 변수(static 변수)의 초기화나 외부 리소스에 대한 연결 설정과 같은 초기화 작업에 유용하게 활용된다.
예시 코드는 다음과 같다.
public class StaticBlockExample {
static {
// 정적 블록 내에서 초기화 작업 수행
System.out.println("This is a static block.");
}
public static void main(String[] args) {
// 클래스를 사용하는 메인 메서드
System.out.println("Main method is called.");
}
}
람다 표현식은 자바 8부터 도입된 기능으로, 주로 함수형 인터페이스(Functional Interface)와 함께 사용된다. 함수형 인터페이스는 하나의 추상 메서드만을 가지며, 람다 표현식은 이 추상 메서드의 구현을 제공한다.
예시 코드는 다음과 같다.
@FunctionalInterface
interface Calculator {
int calculate(int a, int b); // 단일 추상 메서드
}
public class LambdaExample {
public static void main(String[] args) {
Calculator addition = (a, b) -> a + b; // 람다 표현식으로 추상 메서드 구현
System.out.println("Addition: " + addition.calculate(5, 3));
}
}
일반적으로, 추상 클래스를 사용해야만 하는 상황이 아니라면 인터페이스 사용을 권장한다고 한다. 이때, 추상 클래스를 사용해야하는 상황이란
라고 한다.
이외에는 추상 클래스보다 인터페이스가 더 추상적인 존재이기 때문에 인터페이스의 사용이 권장된다.
참고 : https://www.baeldung.com/java-interface-default-method-vs-abstract-class, https://school.programmers.co.kr/learn/courses/17778/17778-%EC%8B%A4%EB%AC%B4-%EC%9E%90%EB%B0%94-%EA%B0%9C%EB%B0%9C%EC%9D%84-%EC%9C%84%ED%95%9C-oop%EC%99%80-%ED%95%B5%EC%8B%AC-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4