JAVA Interface에서 접근 제한자 public과 private 활용하기
이전 회사에서 개발하면서 인터페이스를 사용할 때, 어떤 메소드는 public 접근 제한자를 명시하고, 어떤 메소드는 접근 제한자를 사용하지 않은 추상 메소드가 있었다. 인터페이스를 사용하면서 그 차이를 잘 알지 못했기 때문에 이 글에서 접근 제한자가 인터페이스 내에서 어떻게 사용되며, 이러한 사용이 어떤 의미를 가지는지 정리해봤다.
자바 인터페이스의 메소드 유형
자바 인터페이스는 코드의 유연성과 재사용성을 높이는 핵심적인 요소이다. 자바 8 이전과 이후의 버전에서 인터페이스의 역할과 구조는 중요한 변화를 겪었다.
자바 인터페이스는 소프트웨어 설계에서 중요한 역할을 수행한다. 특히, 접근 제한자의 사용은 인터페이스의 구조와 기능에 큰 영향을 미친다. 자바 8 이전과 이후의 인터페이스에서 public과 private 접근 제한자의 사용 방법은 다음과 같다.
자바 8에서는 인터페이스의 모든 메소드가 기본적으로 public이며 추상적이었다. 즉, 모든 인터페이스 메소드는 public 접근 제한자를 명시적으로 선언해야 했다. 이는 인터페이스가 정의하는 계약에 모든 구현 클래스가 명확하게 응답해야 한다는 원칙을 반영한다.
하지만 자바 9부터는 이러한 요구사항에 변화가 생겼다. 자바 9 이상부터는 인터페이스에 추상 메소드를 선언할 때 public 접근 제한자를 명시적으로 선언하지 않아도 된다. 추상 메소드는 기본적으로 public이며, 이를 명시적으로 선언하지 않아도 자동으로 public으로 간주된다. 이런 변경으로 코드의 간결성이 높아졌다.
💡 여기서 말하는 계약과 public 접근 제한자와의 관계
자바에서 인터페이스는 일종의 계약으로 볼 수 있다. 이 계약은 인터페이스를 구현하는 모든 클래스가 준수해야 하는 규칙과 요구사항을 정의한다. 인터페이스에 선언된 메소드들은 구현 클래스가 반드시 구현해야 하는 '약속된 행동'을 나타낸다. 이 약속을 통해 일관된 방식으로 객체들이 상호 작용할 수 있도록 한다.
자바 8 이전에는 인터페이스의 모든 메소드가 기본적으로 public이었다. 이는 인터페이스가 정의하는 '계약'이 외부에 공개되어야 하며, 인터페이스를 구현하는 모든 클래스가 이러한 메소드들을 구현해야 함을 의미한다. public 접근 제한자는 이러한 메소드들이 인터페이스의 외부, 즉 다른 클래스에서도 접근 가능함을 보장한다.
결론적으로, 인터페이스의 모든 메소드가 public이어야 하는 규칙은 이러한 계약을 외부에 명확하게 드러내고, 인터페이스를 구현하는 모든 클래스가 이 계약을 준수하도록 강제하는 역할을 한다. 이러한 접근은 객체지향 프로그래밍의 기본 원칙과 일관되며, 코드의 유지보수와 확장성에 기여한다.
자바 9에서 도입된 private 메소드는 인터페이스의 재사용성과 유지보수성을 크게 향상시켰다. 이러한 메소드들은 인터페이스 내부에서만 사용되며, 중복 코드를 줄이는 데 기여한다.
private 메소드는 인터페이스의 구현 세부 사항을 숨기고, 인터페이스의 공개 API를 깔끔하게 유지하는 데 도움을 준다.
중복 코드 감소
인터페이스 내의 private 메소드는 공통된 기능을 한 곳에 모아두어 여러 default 메소드에서 재사용할 수 있게 한다. 이는 코드 중복을 감소시키고, 유지보수를 용이하게 만든다. 예를 들어, 여러 default 메소드에서 공통적으로 사용되는 계산 로직이나 데이터 처리 로직을 private 메소드로 분리할 수 있다.
API의 명확성 유지
private 메소드는 인터페이스의 공개 API에서 숨겨진다. 이로 인해 인터페이스를 사용하는 클라이언트에게는 더 간결하고 명확한 API가 제공된다. 클라이언트는 인터페이스의 핵심 기능에만 집중할 수 있으며, 내부 구현 세부 사항에 신경 쓸 필요가 없다.
내부 구현의 캡슐화
private 메소드는 인터페이스의 내부 구현을 캡슐화한다. 이는 인터페이스의 변경이 클라이언트 코드에 미치는 영향을 최소화하며, 내부 구현 변경 시에도 공개 API는 안정적으로 유지될 수 있다.
유연한 리팩토링
인터페이스의 private 메소드는 개발자가 내부 로직을 자유롭게 리팩토링할 수 있게 한다. 이는 기능의 추가나 수정이 필요할 때 유연하게 대응할 수 있도록 하며, 전체적인 코드의 품질을 향상시킨다.
테스트 용이성
공통 로직을 private 메소드로 분리함으로써, 테스트가 더 용이해진다. 개별 기능을 분리함으로써 단위 테스트를 작성하기가 더 쉬워지며, 코드의 안정성과 신뢰성을 높일 수 있다.
인터페이스 예시 코드
아래는 모든 접근 제한자를 사용한 예시 코드다.
public interface AdvancedCalculator {
int add(int a, int b); // 추상 메소드, 구현 필요
default int addThenMultiply(int a, int b, int multiplier) {
int sum = add(a, b);
return multiply(sum, multiplier);
}
private int multiply(int number, int multiplier) {
return number * multiplier; // 곱셈 연산, 인터페이스 내부에서만 사용
}
}
AdvancedCalculator 인터페이스는 세 가지 메소드를 정의한다.
add는 추상 메소드로, 두 숫자의 합을 반환한다.
addThenMultiply는 default 메소드로, 두 숫자를 더한 후 주어진 수로 곱하는 로직을 구현한다.
multiply는 private 메소드로, 곱셈 연산을 수행한다. 이 메소드는 addThenMultiply 내부에서 사용되며, 인터페이스 외부에서는 접근할 수 없다.
AdvancedCalculator 인터페이스 구현체
AdvancedCalculator 인터페이스를 구현하는 구현체를 만들고, 해당 메소드를 실행하여 출력해보자.
먼저, AdvancedCalculator 인터페이스를 구현하는 SimpleCalculator 클래스를 만들었다. 이 클래스는 add 메소드를 구현하고, 인터페이스의 default 메소드인 addThenMultiply를 사용할 것이다.
public class SimpleCalculator implements AdvancedCalculator {
@Override
public int add(int a, int b) {
return a + b;
}
// addThenMultiply 메소드는 이미 인터페이스에서 구현되어 있으므로 여기서는 오버라이드할 필요가 없다.
}
이제 SimpleCalculator 클래스의 인스턴스를 생성하고, add와 addThenMultiply 메소드를 호출하여 결과를 출력하는 메인 메소드를 작성한다.
public class CalculatorDemo {
public static void main(String[] args) {
SimpleCalculator calculator = new SimpleCalculator();
// add 메소드 호출
int sum = calculator.add(5, 10);
System.out.println("5 + 10 = " + sum);
// addThenMultiply 메소드 호출
int result = calculator.addThenMultiply(5, 10, 2);
System.out.println("5와 10을 더한 후 2로 곱한 값: " + result);
}
}
예상 출력값
5 + 10 = 15
5와 10을 더한 후 2로 곱한 값: 30
이 코드는 SimpleCalculator 클래스가 AdvancedCalculator 인터페이스를 어떻게 구현하고 있는지 보여주며, 두 메소드의 작동 방식과 결과를 확인할 수 있게 해준다
결론
자바 인터페이스에서 public과 private 접근 제한자의 사용은 코드의 명확성, 재사용성, 유지보수성을 향상시킨다. 이러한 특성들은 대규모 소프트웨어 프로젝트에서 특히 중요하다. 인터페이스를 통해 정의된 API는 외부 클라이언트에 안정적인 서비스를 제공하며, private 메소드는 내부 구현을 깔끔하게 유지하는 데 기여한다.
XSS 공격 방어: 입력 데이터 이스케이프, CSP, HTTPOnly 설정
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
AtomicInteger를 활용한 파일 저장 순서 동기화 문제 해결 (0) | 2024.02.04 |
---|---|
[JAVA] 코드 최적화를 위한 매직 상수 사용법 (4) | 2023.12.31 |
[JAVA] 람다 표현식에서 final 변수나 effectively final 변수를 사용해야 하는 이유 (0) | 2023.12.01 |
[JAVA] 리플렉션(Reflection)의 이해 (1) | 2023.11.08 |
[JAVA] Java와 Spring, JPA에서의 프록시 객체 이해하기 (1) | 2023.11.08 |