인터페이스
인터페이스는 구현된 것이 없고, 선언만 있는 메소드로 구성. 즉, 실제 로직이 없는 abstract 메소드의 모음
구현하고자 하는 클래스에서 implements로 받아 기능을 반드시 구현해야 사용이 가능하다.
다음 코드로 예시를 들어보겠다.
[ 인터페이스 ]
public interface AnimalInterface1 {
void makeSound();
}
인터페이스는 클래스에 특정 메소드가 있었으면 좋겠다고 명시하는 것이다. 함수 이름만 명시만 하고 구현은 나몰라라 한다.
[ 구현체 ]
public class DogImplements implements AnimalInterface1{
@Override
public void makeSound() {
System.out.println("멍멍");
}
}
구현체 클래스에서 인터페이스를 implements로 받아, 명시된 기능들을 구현한다.
인터페이스는 모든 메서드가 abstract 성질을 가지고 있기 때문에 반드시 명시된 모든 메서드를 구현해야 한다.
[ main ]
public class InterfaceMain {
public static void main(String[] args) {
DogImplements myDog = new DogImplements();
myDog.makeSound();
}
}
main 함수에서 최종적으로 "구현체"를 통해 객체를 만들어, 실제 구현된 메소드를 호출한다.
그렇다면 인터페이스를 쓰는 이유는 뭘까?
위 코드에 같은 구조를 가진 Cat을 추가해보겠다.
[ 구현체 ]
public class CatImplements implements AnimalInterface1{
@Override
public void makeSound() {
System.out.println("야옹");
}
}
[ main ]
public class InterfaceMain {
public static void main(String[] args) {
CatImplements myCat = new CatImplements();
myCat.makeSound();
}
}
같은 인터페이스를 implements하고 있고, 메소드를 재정의할 때 "야옹"이 출력되게 만들었다.
먼저, 강력한 장점에 대해 설명하려면 "다형성"이라는 단어를 알아야 한다.
다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다.
자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현하고 있다.
어려운 말 같지만 위에 상황에서
인터페이스(메서드 이름을 물려주는 부모 클래스)클래스이름 객체이름 = new 구현체(메서드를 구현하는 클래스) 클래스이름
ex) AnimalInterface1 = myCat = new CatImplements(); 처럼 써준다는 것이다.
public class InterfaceMain {
public static void main(String[] args) {
// CatImplements myCat = new CatImplements();
AnimalInterface1 myCat = new CatImplements();
myCat.makeSound();
// DogImplements myDog = new DogImplements();
AnimalInterface1 myDog = new DogImplements();
myDog.makeSound();
}
}
그래서 도대체 왜 이렇게 쓰는건데?
1) 인터페이스를 통해 공통된 기능 구현을 강제하면서 코드를 표준화시킬 수 있다.
객체가 Dog이든지 Cat이든지 울음소리를 출력하는 공통된 메소드를 통해, 메소드를 호출하는 객체에 따라 해당 동물의 울음소리를 알 수 있다.
2) 다형성 특징으로, 기능을 확장하거나 변경하는 것이 용이하다.
부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조하는 다형성을 보장하면서
myCat만 제공되던 서비스에서 myCat은 전부 없애고, 같은 기능을 하는 myDog 서비스만 제공해야 할 경우
AnimalInterface1 myCat = new CatImplements();
AnimalInterface1 myDog = new DogImplements();
같은 인터페이스를 상속받는 myDog만 구현해 위와 같이 한 단어의 코드만 수정해줄 수 있어
변경 사항이 발생하더라도 유연하게 대처할 수 있다.
3) 클래스 간의 관계를 인터페이스로 연결하며, 클래스마다 독립적인 프로그래밍 가능하다.
Dog과 Cat은 서로가 서로에게 전혀 영향을 주지 않는다. 한 쪽에 변경이 생겨도 다른 쪽을 신경써주지 않아도 된다.
4) 다중 상속이 가능하다.
아래와 같이 새로운 인터페이스를 추가했다.
public interface AnimalInterface2 {
String play(String a, String b);
}
새로운 클래스를 만들어 makeSound가 명시된 인터페이스1과, play가 명시된 인터페이스2를 모두 상속시켜줬다.
class DogMultiImplements implements AnimalInterface1, AnimalInterface2{
@Override
public void makeSound() {
System.out.println("멍멍");
}
@Override
public String play(String a, String b) {
return a + "와 " + b + "가 놉니다.";
}
}
[ main ]
public class InterfaceMain {
public static void main(String[] args) {
DogMultiImplements myMultiDog = new DogMultiImplements();
myMultiDog.makeSound();
System.out.println(myMultiDog.play("자원", "호두"));
}
}
클래스는 다중 상속이 불가능했지만, 인터페이스는 가능하다는 것을 알 수 있다.
클래스가 다중 상속이 불가능한 이유는 부모 클래스에 이름이 같은 일반 메서드가 있으면, 메소드 호출 시 어떤 부모의 일반 메서드를 불러올 것인지 "모호성"이 발생하기 때문이다.
하지만, 인터페이스는 부모 클래스에 같은 이름의 메소드가 있어도 자식 클래스에서 "반드시" 재정의(오버라이딩)하기 때문에 같은 이름의 함수가 있어도 재정의하면서 덮어쓰기 된다.(이전에 구현된 기능들이 무시된다)
때문에 여러 개의 인터페이스를 상속받으면서 하나의 객체를 다양하게 표현할 수 있게 되었기 때문에 확장성이 좋아졌고, 클래스 간의 계층적 분류 및 관리가 가능하여 유지보수성이 좋다.
'Language > JAVA' 카테고리의 다른 글
[JAVA] 날짜 관련 클래스 Calendar와 LocalDateTime, ChronoField 사용방법 (1) | 2023.12.27 |
---|---|
[JAVA] Comparator와 Comparable 이해하기 (1) | 2023.12.22 |
자바 클래스와 객체 관계에서 스프링적 사고하기 (1) | 2023.12.17 |
[JAVA] 자바 그래프 최단거리(BFS) (0) | 2023.03.29 |
[JAVA] 자바 그래프 경로탐색 DFS 인접행렬, 인접리스트 (0) | 2023.03.27 |