Skip to content

Latest commit

 

History

History
201 lines (162 loc) · 8.11 KB

인터페이스.md

File metadata and controls

201 lines (162 loc) · 8.11 KB

인터페이스 (interface)

인터페이스는 일종의 추상클래스지만 추상클래스보다 추상화 정도가 높다. 인터페이스는 오직 추상메서드와 상수만을 멤버로 가질 수 있다. 인터페이스를 작성시에는 class대신 interface를 사용한다. 그리고 클래스와 같이 접근제어자로 public과 default를 사용할 수 있다.

인터페이스는 인터페이스로부터만 상속받을 수 있고 클래스와 다르게 다중상속이 가능하다.

다중상속이 인터페이스에서 가능한 이유는 다중상속이 가능하지만 실제로 다중상속을 구현하는 경우는 거의 없다.

  • 모든 멤버변수는 public static final이야하며, 이를 생략할 수 있다.
  • 모든 메서드는 public 이어야 하며, 이를 생략할 수 있다. 자바8 이후 default, abstract, static으로 메서드로 선언가능해졌다. 이전에는 무조건 abstract 메서드여야 했다.

인터페이스 구현

인터페이스도 추상클래스와 마찬가지로 이 자체로 인스턴스를 생성 할 수 없으며 자신에게 정의된 추상메서드의 몸통을 만들어주는 클래스를 만들어줘야 한다. 이때, 인터페이스는 implements라는 키워드를 사용한다. 또, 그 class에서 interface 구현한다면 interface의 모든 method들을 구현해야 한다.

interface I{
    public void z();
}
 
class A implements I{
    public void z(){}
}

class A에서 반드시 public void z()메소드를 포함하고 있어야 한다는 뜻이다.


인터페이스가 필요한 이유

개발자 A와 B가 계산기 기능이 필요한 프로젝트를 진행했다고 가정해보자. 개발자 A는 계산기를 사용하는 로직을 만들고 개발자 B가 계산기 클래스를 만들었다고 가정해보자.

개발자 A는 계산기 클래스가 완성되기까지 기다려야하는데 개발기간이 촉박해 임의로 CalculatorDummy클래스를 만들고 개발을 진행했다.

class CalculatorDummy{
    public void setOprands(int first, int second, int third){}
    public int sum(){
        return 60;
    }
    public int avg(){
        return 20;
    }
}
public class CalculatorConsumer {
    public static void main(String[] args){
        CalculatorDummy c = new CalculatorDummy();
        c.setOprands(10,20,30);
        System.out.println(c.sum()+c.avg());
    }
}

그리고 개발자 B는 계산기 클래스를 아래와 같이 만들었다.

class Calculator {
    int left, right;
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
    public void sum() {
        System.out.println(this.left + this.right);
    }
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}

하지만, calculatorDummy의 setOprands는 3개의 변수를 받고 실제 Calculator의 setOprands는 2개를 받는 것을 볼 수 있다. 이렇게 되면 두 개발자중에 한 개발자의 코드를 바꿔야하고 다툼이 일어날 수 있다.

이런 문제를 해결하기 위해서 개발자간의 구체적인 약속을 코드로 정해보자 해서 나온것이 interface이다.

Calculator클래스를 개발하는 개발자가 interface를 작성해 이 클래스가 가질 메서드를 인터페이스를 만들어 제공하는 것이다. 반대의 경우도 가능하다.

public interface Calculatable {
    public void setOprands(int first, int second, int third) ;
    public int sum(); 
    public int avg();
}

인터페이스의 규칙

  1. 하나의 인터페이스가 여러개의 인터페이스를 구현할 수 있다.
interface I1{
    public void x();
}
 
interface I2{
    public void z();
}
 
class A implements I1, I2{
    public void x(){}
    public void z(){}   
}

class A는 두 interface 모두 구현해야한다. 하나라도 구현하지 않을시 오류가 난다.

  1. 인터페이스도 상속이 된다.
  2. 인터페이스가 default로 선언된 경우 메소드가 선언될 수 있다.

이경우 이를 구현하는 class는 default메서드를 오버라이딩 할 수 있다. 또, 다중상속을 하고 있는데 이 인터페이스들에 동일한 default 메서드가 존재시에 해당 메서드를 무조건 오버라이딩해야한다. 만일 하지 않을시 컴파일 에러가 뜬다.


인터페이스와 다형성

  • 한 클래스의 데이터 타입으로 인터페이스를 지정할 수 있다.
  • 클래스의 데이터 타입으로 한 인터페이스로 지정함으로써 해당 인터페이스의 멤버만 접근 가능하게 한다.
interface I2{
    public String A();
}

interface I3{
    public String B();
}

class D implements I2, I3{
    public String A(){
        return "A";
    }
    public String B(){
        return "B";
    }
}

public class PolymorphismDemo3 {
    public static void main(String[] args) {
        D obj = new D();

        // 이렇게 클래스 D의 데이터 타입으로 인터페이스 I2, I3이 각각 될 수 있다.
        I2 objI2 = new D();
        I3 objI3 = new D();
        
        // obj인스턴스의 경우 데이터타입이 D이기때문에 클래스 D가 정의하고 있는 모든 멤버타입을 사용할 수 있다.
        obj.A();
        obj.B();
        
        // 하지만 objI2는 데이터 타입이 I2이기때문에 objI2는 마치 메서드 A만을 가지고 있는 클래스 인것처럼 동작한다. 따라서 objI2.B는 I3에 정의 되어 있기때문에 에러가 발생한다.
        objI2.A();
        //objI2.B();  => error
         
        // 하지만 objI3는 데이터 타입이 I3이기때문에 objI3는 마치 메서드 B만을 가지고 있는 클래스 인것처럼 동작한다. 따라서 objI3.A는 I2에 정의 되어 있기때문에 에러가 발생한다. 
        //objI3.A();   => error
        objI3.B();
    }
}

조금 더 이해를 하기 위해 아래의 코드를 보자.

interface father{}
interface mother{}

interface programmer{
    public void coding();
}
interface believer{}

class Steve implements father, programmer, believer{
    public void coding(){
        System.out.println("fast");
    }
}

class Rachel implements mother, programmer{
    public void coding(){
        System.out.println("elegance");
    }
}

public class Workspace{
    public static void main(String[] args){
        programmer employee1 = new Steve();
        programmer employee2 = new Rachel();
         
        employee1.coding();
        employee2.coding();
    }
}

사람은 여러가지의 역할을 가지고 있다. 위에 나온 스티브랑 레이첼 또한 마찬가지다. 스티브는 아빠이자, 프로그래머, 그리고 종교단체에서 신자이다. 레이첼은 엄마이자 프로그래머이다.
레이첼과 스티브는 같은 직장을 다닌다. 그 직장에서는 레이첼과 스티브가 프로그래머라는 사실만 중요하다. 따라서 위 처럼 programmer인터페이스를 데이터 타입으로 정의해서 레이첼이 엄마든 아빠든 종교를 믿는 사람이건은 중요하지 않다. 두 사람은 각각 다른 방법으로 coding을 구현한다. 스티브는 빠르게 또 레이첼은 엘레강스하게 구현한다.


abstract vs interface

  • 추상클래스는 구체적인 로직이나 상태를 가질 수 있는 반면 인스턴스는 구체적인 로직이나 상태를 가질 수 없다.

  • 추상클래스는 서로 관계가 깊은 객체에 사용된다(부모, 자식). 반면, 인터페이스는 관련없는 클래스에 공통된 기능을 제공하는데 적합하다.

Q. 왜 service 는 interface를 따로 만들까?

그 이유는 기존 구현객체(serviceImpl1)와 비즈니스 로직이 다른 기능을 추가해야 하는 경우 다른 구현체(serviceImpl2)를 만들어 사용할 수 있다. 이렇게 유지보수 측면에서 유리하다.




참고자료