[JAVA]인터페이스(Interface)에 대해 알아보자

Inung_92·2023년 2월 20일
1

JAVA

목록 보기
9/15
post-thumbnail

인터페이스란?

📖다른 클래스를 작성할 경우 기본이되는 틀을 제공하면서 다른 클래스 사이의 중간매개 역할까지 담당하는 일종의 추상클래스를 의미.

인터페이스는 프로그램 내 다양한 기능을 하는 클래스들에게 기본이 되는 틀을 제공한다. 이 말이 무엇인지 간단한 예시를 들어보겠다. 아래는 Animal이라는 인터페이스가 있으며, 이를 구현하는 Dog와 Cat의 코드이다.

🖥️ 인터페이스

//인터페이스 선언
public interface Animal{
	//추상 메소드 선언
	public void move();
}

🖥️ 구현 클래스

//Dog 클래스 선언
public class Dog implements Animal{
	//Animal의 추상 메소드 구현
	@Override
    public void move(){
    	System.out.println("개가 움직입니다.");
    }
}

//Dog 클래스 선언
public class Cat implements Animal{
	//Animal의 추상 메소드 구현
	@Override
    public void move(){
    	System.out.println("고양이가 움직입니다.");
    }
}

위 코드에서 Animal이 Dog와 Cat에게 제공하는 기본 틀은 무엇일까? 바로 move()라는 추상메소드이다. 인터페이스는 클래스들이 공통적으로 구현해야할 메소드에 대하여 강제구현 의무를 부여한다. 이 말은 공통적으로 수행해야할 기본적인 기능을 인터페이스가 정의하여 제공한다는 말과도 같다.

⚡️ 인터페이스의 필요성

먼저 클래스와 클래스간의 is a 관계는 상속(extends)이다. 중요한 부분은 하나의 클래스는 하나의 슈펴클래스만을 상속받을 수 있다. 즉, 다중상속이 지원되지 않는 것이다. 이러한 문제로 구현해야할 메소드의 분할이 필요하거나 클래스들에게 다중상속이 필요할 때 인터페이스를 사용한다.
다시 말하면 클래스는 특정 클래스의 상태나 동작의 범위에서 크게 벗어나지 않거나 거의 유사한 기능을 수행하는 하위 클래스를 선언하여 사용할 경우 상속으로 관계를 맺는다. 하지만 수행해야하는 동작은 유사하나 다양한 성격의 클래스가 필요하다면 인터페이스를 통해 각 클래스가 인터페이스의 추상 메소드를 구현하여 클래스들을 하나의 자료형을 묶어주는 것이다.

⚡️ 인터페이스의 장점

인터페이스의 대표적인 장점에 대해서 알아보자.

  • 대규모 프로젝트 개발 시 일관되고 정형화된 개발을 위한 표준화 가능
  • 클래스의 작성과 인터페이스 구현을 동시에 진행할 수 있으며, 더 유연한 코드작성과 개발시간이 단축
  • 클래스와 클래스 간의 관계를 인터페이스로 연결하여 클래스마다 독립적인 프로그래밍 가능

예를 들어 현실세계에서 자동차를 기준으로 예를 들어보도록하자. 아래 그림을 보면 이해가 쉬울 것이다.

위 그림을 보면 아우디, 벤츠, 기아의 캠핑카가 있다. 여기서 각 캠핑카들은 기본적으로 자동차라는 최상위 카테고리에 포함되어있다. 이와 동시에 캠핑카라는 특수성을 가지고 있는 것이다. 기본적으로는 자동차이면서 캠핑에 특화된 자동차가 되는 것이다.
이런 상황에서 캠핑카들이 갖춰야할 기본적인 기능들의 틀을 제공하는 것이 현재 예시에서 CamPingCar 인터페이스인 것이다.
이러한 예시를 전기차로 바꿔도 동일할 것이다. 자동차이면서 전기로 제작된 특수성을 가지고 있는 것이다.


인터페이스의 사용

이제부터 인터페이스가 어떻게 사용되는지 알아보자. 예시는 개념적인 부분만 간단하게 작성하겠다.

⚡️ 기본문법

public interface 인터페이스_명{
	//상수 정의
	public static final 자료형 상수_명 =;
    //추상메소드 정의
    public 반환형(void, int 등등) 메소드_명(매개변수);
    //default 메소드 정의(자바 8부터 지원)
    public default 반환형 메소드_명(매개변수){
    	실행코드 작성 ...
    };
}

인터페이스는 기본적으로 상수와 추상 메소드를 보유할 수 있으며, 자바 8 이후로는 디폴트 메소드까지 보유할 수 있게되었다. 여기서 몇가지 짚어보고 넘어가자.

  • 클래스와 달리 인터페이스의 모든 필드는 public static final로 선언한다. 즉 모든 필드는 상수이다.
  • 추상메소드의 abstract키워드는 생략가능하며, 키워드 생략 시 추상메소드로 인식.
  • default 메소드는 실행코드까지 정의하며, 구현 객체에서 해당 메소드를 호출하여 사용 가능.

다음은 인터페이스를 구현해보자.

⚡️ 인터페이스의 구현

인터페이스는 추상 클래스와 마찬가지로 직접 new 연산자를 통해 인스턴스를 생성할 수 없다. 인터페이스를 구현하는 구현 객체를 통해서만 구현이 가능하다.
또한, 인터페이스는 보유한 추상메소드를 구현 객체에게 강제구현을 할 것을 요구한다. 아래 코드를 보자.

//인터페이스 선언
public interface Animal{
	//추상메소드 선언
	public void cry();
    //디폴트 메소드 선언
    public default void introduce(){
    	//실행코드 작성
    	System.out.println("나는 동물이다");
    }
}

//구현 클래스 선언
public class Cat implements Animal{
	//추상 메소드 강제구현
    @Override
    public void cry(){
    	System.out.println("야옹");
    }
}
public class Dog implements Animal{
	//추상 메소드 강제구현
    @Override
    public void cry(){
    	System.out.println("멍멍");
    }
}

public class AnimalTest{
	public static void main(String[] args){
    	Cat cat = new Cat();
        Dog dog = new Dog();
        
        //오버라이딩 메소드 호출
        cat.cry();
        dog.cry();
        
        //디폴트 메소드 호출
        cat.introduce();
        dog.introduce();
    }
}

//실행결과
"야옹"
"멍멍"

"나는 동물이다"
"나는 동물이다"

인터페이스의 추상 메소드를 강제 구현하는 것은 쉽게 설명하여 메소드를 오버라이딩 하는 것이다. 오버라이딩을 할 때 주의사항은 다음과 같다.

  • 반환타입, 메소드명, 매개변수를 동일하게 작성
  • 추상메소드이므로 예외처리는 자유롭게 가능
  • 디폴트 메소드는 구현객체에서 호출하여 사용이 가능하며, 기존 메소드와 이름이 중복되어 충돌할 경우 메소드를 오버라이딩

디폴트 메소드에 대해서는 잠시 뒤 자세히 설명하겠다.

⚡️ 인터페이스의 다형성

다음으로 인터페이스의 다형성은 어떻게 지원되는지 살펴보자. 위 코드에서 작성된 예시를 활용하여 살펴보겠다.

//인터페이스 타입으로 클래스 생성 가능
Animal animal = new Cat();

//인터페이스 자료형을 매개변수로 전달 가능
public void send(Animal animal){
	생략...
}

//리턴 타입을 인터페이스 자료형 리턴 가능
public Animal returnInterface(){
	Animal animal = new Cat();
	return animal;
}

인터페이스의 다형성은 스프링 프레임워크를 사용하는 경우 의존성 주입에 중요한 역할을 한다. 인터페이스를 구현한 객체의 인스턴스를 반환한다던지 인터페이스 자체를 매개변수로 지정하여 다양한 구현 객체들을 전달한다던지의 활동을 할 수 있다.

⚡️ default 메소드

자바 8 이전까지 인터페이스는 상수와 추상 메소드만을 보유하였다. 하지만 자바 8 이후로 디폴트 메소드가 지원되면서 인터페이스에도 기본적인 구현내용을 포함 할 수 있게 된 것이다. 기본적인 문법은 위에 설명하였으니 이후 단계부터 차근차근 알아가보자.

🖥️ 디폴트 메소드 구현

//인터페이스 선언
public interface Animal{
    //디폴트 메소드 선언
    public default void introduce(){
    	//실행코드 작성
    	System.out.println("나는 동물이다");
    }
}

//구현 클래스 선언
public class Cat implements Animal{
	public static void main(String[] args){
    	Cat cat = new Cat();
        //디폴트 메소드 자동 구현
        cat.intoroduce();
    }
}

디폴트 메소드 구현 시 인터페이스에서 정의된 내용을 구현 객체에서 그대로 구현을 할 수 있으며 혹시 별도의 재정의가 필요한 경우 오버라이드하여 사용할 수 있다.

🖥️ 디폴트 메소드 구현 시 주의사항

그렇다면 동일한 디폴트 메소드명을 가진 2개의 인터페이스를 구현하는 클래스는 어떻게 해야할까? 개념적으로는 다중구현이기 때문에 컴파일러는 어떤 인터페이스의 메소드를 구현하는 것인지 헷갈릴 수 있다. 이럴 때는 디폴트 메소드를 오버라이드 해주면된다.

//인터페이스1 선언
public interface Animal1{
    //디폴트 메소드 선언
    public default void introduce(){
    	//실행코드 작성
    	System.out.println("나는 동물1이다");
    }
}
//인터페이스2 선언
public interface Animal2{
    //디폴트 메소드 선언
    public default void introduce(){
    	//실행코드 작성
    	System.out.println("나는 동물2이다");
    }
}

//구현 클래스 선언
public class Cat implements Animal1, Animal2{
	@Override
    public void introduce(){
    	Animal1.super.introduce();
        Animal2.super.introduce();
        System.out.println("나는 고양이다");
    }

	public static void main(String[] args){
    	Cat cat = new Cat();
        //디폴트 메소드 자동 구현
        cat.intoroduce();
    }
}

//실행결과
"나는 동물1이다"
"나는 동물2이다"
"나는 고양이다"

위와 같이 동일한 디폴트 메소드명을 가진 인터페이스를 다중 구현한 객체가 해당 메소드를 오버라이드한 경우 메소드 3개가 동시에 출력된다. 이렇게 동일한 메소드명을 가진 경우에는 구현하는 객체에서 해당 메소드를 오버라이드하여 사용하면 컴파일 에러를 방지할 수 있다. 만약 그렇지 않을 경우 아래와 같은 컴파일 에러가 발생한다.

Error:(33, 16) java: class Car inherits unrelated defaults for doSomething(int) from types Animal1 and Animal2

그렇다면 인터페이스를 다중구현한 객체랑은 다르게 클래스를 상속받고 인터페이스를 구현한 객체가 동일한 메소드명을 가진 상황이라면 어떨까?
이런 경우에는 자바에서 다중상속을 지원하지 않는 특성과 상속과 구현을 다르게 처리하는 특성을 생각하면된다. 결과 먼저 이야기하자면 컴파일러는 메소드명이 동일하여 충돌이 발생할 경우 상속한 클래스의 메소드에 우선순위가 더 높다고 판단하고 해당 메소드명를 호출한다.

위 설명은 별도 코드로 작성할 필요가 없기 때문에 예시코드는 작성하지 않겠다.


마무리

이렇게해서 이번 게시글에서는 인터페이스에 대하여 알아보았다. 클래스를 먼저 작성하고 인터페이스를 작성하려했지만 공부를 하다보니 정리를 안하면 안될 것 같아서 먼저 작성한 점을 이해해주길 바란다.

인터페이스를 간단하게 다시 정리해보자.

  • 클래스들의 기본 틀을 제공하는 일종의 추상클래스를 의미.(엄연히 말하자면 추상 클래스는 아니다.)
  • 상수, 추상메소드, 디폴트메소드만을 보유할 수 있음.(자바 8부터 디폴트 메소드 지원)
  • 다중 구현이 가능하며 인터페이스는 인터페이스만을 상속받을 수 있음.
  • 다형성을 지원하며 구현 객체의 인스턴스로 주소값 공유가 가능하고, 리턴 타입으로 사용될 수 있으며 매개변수로 전달도 가능

인터페이스를 기존 Java SE에서는 잘 사용하지 않았지만 Java EE를 사용하고, 스프링 프레임워크를 배우는 현 시점에서는 굉장히 중요한 개념으로 자리잡고 사용 빈도도 높아지고 있음. 인터페이스를 통해 클래스 다중상속의 단점을 보완할 수 있으며 상황에 따라 하나의 클래스가 다양한 특성을 가질 수 있다는 것이 장점이라고 생각이 된다.
다만, 단일책임 원칙에 위배되지 않도록 인터페이스를 사용하는 것에는 조금 더 세밀한 노력이 필요하다.

그럼 이만.👊🏽

profile
서핑하는 개발자🏄🏽

0개의 댓글