JAVA

[Java] 인터페이스란?

로춘남 2022. 3. 25. 09:48
728x90


인터페이스(interface)란?

자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가질 수 있습니다. 하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속은 지원하지 않습니다.

그렇지만 다중 상속의 이점을 버릴수는 없기에 Java에서는 interface 를 통해 다중 상속을 지원하고 있습니다. 인터페이스란? 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미합니다. 

자바의 추상 클래스는 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함을 할 수 있습니다. 하지만 인터페이스는 오직 추상 메소드와 상수만을 포함 할 수 있습니다.

 

인터페이스의 선언

자바에서 인터페이스를 선언하는 방법은 클래스를 작성하는 방법과 같습니다. 인터페이스를 선언할 때는 접근 제어자와 함께 interface 키워드를 사용합니다.

 

ex)

접근제어자 interface 인터페이스이름 {

    public static final 타입 상수이름 = 값;
    ...
    public abstract 메소드이름(매개변수목록);
    ...
}

단, 클래스와는 달리 인터페이스의 모든 필드는 public static final이어야 하며, 모든 메소드는 public abstract이어야 한다. 이 부분은 모든 인터페이스에 공통으로 적용되는 부분이므로 이 제어자는 생략 할 수 있습니다. 이렇게 생략된 제어자는 자바 컴파일시 컴파일러가 자동으로 추가를 해줍니다.

 

인터페이스의 구현

인터페이스는 추상 클래스와 마찬가지로 자신이 직접 인스턴스를 생성할 수는 없습니다. 따라서 인터페이스가 포함하고 있는 추상 메소드를 구현해 줄 클래스를 작성해야 합니다.

ex)

class 클래스이름 implements 인터페이스이름 { ... }

만약 모든 추상 메소드를 구현하지 않는다면, abstract 키워드를 사용하여 추상 클래스로 선언해야 합니다. 다음은 인터페이스를 구현하는 예제를 한 번 살펴보겠습니다.

interface Animal { 
	public abstract void cry(); 
   }

class Cat implements Animal {
    public void cry() {
        System.out.println("냐옹냐옹!");
    }
}

class Dog implements Animal {
    public void cry() {
        System.out.println("멍멍!");
    }
}

public class Polymorphism03 {
    public static void main(String[] args) {
        Cat c = new Cat();
        Dog d = new Dog();
        c.cry();
        d.cry();
    }
}

//[실행 결과]
//냐옹냐옹!
//멍멍!

 

자바는 상속과 구현을 동시에 표현 할 수 있습니다.

ex)

class 클래스이름 extend 상위클래스이름 implements 인터페이스이름 { ... }

인터페이스는 인터페이스로부터만 상속을 받을 수 있으며, 또한 여러 인터페이스들을 상속 받을 수 있습니다. 아래는 인터페이스를 활용한 다중 상속의 example 입니다.

 

interface Animal { 
	public abstract void cry(); 
   }

interface Pet {
	public abstract void play();
   }

class Cat implements Animal, Pet {
    public void cry() {
        System.out.println("냐옹냐옹!");
    }

    public void play() {
        System.out.println("쥐 잡기 놀이하자~!");
    }
}

class Dog implements Animal, Pet {
    public void cry() {
        System.out.println("멍멍!");
    }

    public void play() {
        System.out.println("산책가자~!");
    }
}


public class Polymorphism04 {
    public static void main(String[] args) {
        Cat c = new Cat();
        Dog d = new Dog();

        c.cry();
        c.play();
        d.cry();
        d.play();
    }
}

//실행 결과
//냐옹냐옹!
//나비야~ 쥐 잡기 놀이하자~!
//멍멍!
//바둑아~ 산책가자~!

이 예제는 Cat과 Dog클래스가 각각 Animal, Pet이라는 2 개의 인터페이스를 동시에 구현하고 있습니다.

 

 

 

클래스를 이용한 다중 상속의 문제점

클래스를 이용하여 다중 상속을 하면 같은 메소드 출처의 모호성 등의 문제가 발생 할 수 있습니다.

class Animal { 
    public void cry() {
        System.out.println("짖기!");
    }
}

class Cat extends Animal {
    public void cry() {
        System.out.println("냐옹냐옹!");
    }
}

class Dog extends Animal {
    public void cry() {
        System.out.println("멍멍!");
    }
}

① class MyPet extends Cat, Dog {}
public class Polymorphism {
    public static void main(String[] args) {
        MyPet p = new MyPet();
②      p.cry();
    }
}

예제에서는 Cat과 Dog 클래스가 각각 Animal 클래스를 상속받아 cry() 메소드를 오버라이딩 하고있습니다. 이 과정까지는 문제가 없겠지만 ①번 라인에서 MyPet 클래스가 Cat 클래스와 Dog 클래스를 동시에 상속받게 되면 문제가 발생하게 됩니다.

②번 라인에서 MyPet 인스턴스인 p가 cry() 메소드를 호출하면, 이 메소드가 Cat 클래스에서 상속받은 cry() 메소드인지 Dog 클래스에서 상속받은 cry() 메소드인지를 구분 할 수 없는 모호성을 지니게 됩니다. 이러한 이유로 자바에서는 클래스를 이용한 다중 상속을 지원하지 않습니다.

 

그렇지만 아래 예제처럼 인터페이스를 활용하여 다중 상속을 하게 되면, 위와 같은 메소드 호출의 모호성을 막을 수 있습니다.

interface Animal { 
	public abstract void cry(); 
  }

interface Cat extends Animal { 
	public abstract void cry();
  }

interface Dog extends Animal { 
	public abstract void cry(); 
  }

class MyPet implements Cat, Dog {
    public void cry() {
        System.out.println("멍멍! 냐옹냐옹!");
    }
}

public class Polymorphism05 {
    public static void main(String[] args) {
        MyPet p = new MyPet();
        p.cry();
    }
}

//실행 결과
//멍멍! 냐옹냐옹!

위 예제는 Cat 인터페이스와 Dog 인터페이스를 동시에 구현한 MyPet 클래스에서만 cry() 메소드를 정의하였으므로, 앞선 예제에서 발생한 메소드 호출의 모호성이 발생되지 않습니다.

 

 

인터페이스의 장점

인터페이스를 사용하면 다중 상속이 가능할 뿐만 아니라 다음과 같은 장점을 가질 수 있습니다.

 

1. 대규모 프로젝트 개발 시 일관되고 정형화된 개발을 위한 표준화가 가능.

2. 클래스의 작성과 인터페이스의 구현을 동시에 진행할 수 있으므로, 개발 시간을 단축할 수 있음.

3. 클래스와 클래스 간의 관계를 인터페이스로 연결하면, 클래스마다 독립적인 프로그래밍이 가능.

 

※ 인터페이스와 추상클래스의 차이

  인터페이스 추상클래스
목적 작업지시자 역할(오버라이드 강제) 상속 목적
인스턴스를 생성 할 수 없다.
구현부가 있는 메소드, 구현부가 없는(추상 메소드)를 모두 가질 수 있다.
추상 메소드 형태 명령이 존재해도 무관 명령이 존재하지 않음
상속 가능 개수 다중상속 단일상속
(자바에서 다중상속을 지원하지 않기 때문에)
  • 기능적인 부분
    • 인터페이스의 모든 변수는 public static final 이고(immutable), 모든 메소드는 public abstract 이다.
    • 추상클래스의 변수는 static final이 아닌 필드도 가질 수 있고(mutable), 메소드를 public, private, protected로 선언 할 수 있다.
    • 인터페이스는 다중구현이 가능하다.
    • 인터페이스는 생성자를 정의 할 수 없다.
    • 추상클래스는 생성자를 정의 할 수 있따.
  • 역할적인 부분
    • 인터페이스는 동일한 기능을 다른 방식으로 구현하고자 할때 사용 할 수 있다.(구현 객체가 동일한 동작을 하는 것을 보장)
    • 추상 클래스는 밀접하게 연관된 클래스들 간에 공통된 코드를 사용하고자 할때 사용 할 수 있다.(상속받은 객체가 확장된 동작을함)

 

※ Enum 클래스(나열형)

: 상수필드 선언이 목적인 interface의 문제점을 보완하기위한 참조형. 

  •  상수필드 선언을 위해 만든 인터페이스의 문제점
    • 1) 의미있는 문자열로 표현되지 않음 : 각각 다른 값인것 구분 불가(숫자값이 출력되어 무슨뜻을 가지는지 모름)
    • 2) 값을 대표적으로 표현하기에 부적절 : 상수 필드가 선언된 자료형(클래스)이 무의미
    • 3) 가독성이 떨어지며, 모든 상수를 순회하기 어려움.
  • 나열형을 선언하는 방법 : pulic enum 나열형명 { 변수명, 변수명, 변수명, ... }
    • 단지 변수정을 차례대로 나열만 하면됨.
    • 초기값을 생략하고 변수명만 나열시 선언되는 순서대로 0 ~ 1씩 증가하는 값을 자동으로 지님
    • 값을 직접 지정하는 것도 가능
    • 이렇게 선언하면 같은 숫자값을 가져도 다른 상수 필드(다른 클래스, 변수명)를 구분 할 수 있음.

 

Enum을 사용했을 경우 장점

1) IDE의 지원을 적극적으로 받을 수 있다.(타입체크, 자동완성, 오타검증 등)

2) 리팩토링 범위가 줄어듬

3) 정수열거형 패턴의 단점을 모두 해결 가능


출처 : http://www.tcpschool.com/java/java_polymorphism_interface

728x90