인터페이스

2020. 6. 2. 13:44정리/JAVA


인터페이스란? 

동일한 목적 하에 동일한 기능을 수행하게끔 강제하는 기능이다.

 

상호의존도를 낮추기 위해 어떠한 로직(클래스)를 분리시켰다면, 여러 가지 변수로 인해 개발자의 의도대로 동작하지 않는 것을 방지하는 기능이라고 이해했다.

 

상호의존도가 낮을 시 코드의 수정이 비교적 자유로워지므로 자바 문법 상 문제가 없지만, 로직 상으로는 개발자의 의도에서 벗어날 수 있기 때문이다.

특히, 각 기능을 모듈화하여 진행할 경우 다른 개발자가 구현한 로직을 사용할 때에 자신이 필요한 로직이 다른 로직과 혼합되어 있거나, 다른 방식으로 표현되는 등의 이슈들이 생길 가능성이 존재한다.

 

정리하자면 추상화를 통해 각 객체들의 핵심적인 기능을 추출해 이를 상위 개념으로 정의하고, 상위 개념의 핵심 기능들을 구현하도록 강요함으로써 결합도를 낮춤과 동시에 기능(로직)에 대한 신뢰성을 유지하는 기능이다.

 

인터페이스의 특징은

  • class 키워드 대신 interface 키워드를 사용해 정의
  • 자체 인스턴스 생성 불가 
  • implements(구현)을 통해 사용 
  • 모든 멤버변수에는 public static final 제어자가 추가되어 상수로 처리됨 (생략 가능)
  • 특정 제어자를 추가하지 않은 메소드의 경우 public abstract 제어자가 추가되어 추상 메소드로 처리됨 (생략 가능)
  • 하나의 구현체가 여러 개의 인터페이스를 구현할 수 있음

이 있다. 

 

 

다음으로 인터페이스의 기능을 예제를 통해 알아보도록 하겠다.

 


인터페이스 기능 

개발시간 단축

이 기능은 협업과 관련되어 있다.

 

public interface 회원정보 {
    ...
    
    public String 회원이름(String 회원아이디);
    
    public String 회원주소(String 회원아이디);
    ...
}

 

위와 같은 회원정보와 관련된 인터페이스가 있을 경우, 상품 구매 모듈을 담당하고 있는 개발자가 물품을 구매한 뒤 구매한 회원의 정보를 표기하는 기능을 구현해야 할 경우

 

public void 구매회원정보(String 회원아이디) {
    String 회원이름 = 회원정보.회원이름();
    String 회원주소 = 회원정보.회원주소();
    
    ...
}

위와 같이 회원 이름을 구하는데 어떤 로직이 필요한지 전혀 신경쓰지 않고 단순히 회원정보 인터페이스 구현체의 인스턴스를 생성해 호출하면 바로 원하는 값을 얻을 수 있다.

 

즉, 메소드 구현부와 관계없이 선언부만 알면 개발이 가능하다.

또한 이러한 특징 때문에 협업에 용이하다.

 

표준화 기능 

인터페이스가 동일한 기능을 수행하게끔 강요한다는 특징과 관련되어 있다.

 

public interface a {
    public String 기능1();
    
    public String 기능2();
}

a 인터페이스를 구현한 aImpl 클래스의 경우

 

public class aImpl implements a {
    @Override
    public void 기능1() {
        ...
        내부로직1;
        ...
    }
    
    @Override
    public void 기능2() {
        ...
        내부로직2;
        ...
    }
}

위와 같이 내부 로직과는 상관없이 해당 기능을 사용하기 위한 메소드 시그니처는 모두 동일하다.

 

미리 정해진 규칙(메소드 시그니처)에 맞춰 구현해야하므로 인터페이스를 기본 틀로 코드를 작성하게 되며,

이로 인해 일관되고 정형화된 프로그램을 개발할 수 있다.

 

독립적인 프로그래밍 구현 기능

public interface Musician {
	public void play();
}

 

public class Pianist implements Musician {
    @Override
    public void play() {
    	System.out.println("피아노 연주");
    }
}

 

public class Violinist implements Musician {
    @Override
    public void play() {
    	System.out.println("바이올린 연주");
    }
}

 

public class Cellist implements Musician {
    @Override
    public void play() {
    	System.out.println("첼로 연주");
    }
}

 

public class Tests {
    public static void main(String[] args) {
    
    	Musician musician = new Pianist();
        // Musician musician = new Vilolist();
        // Musician musician = new Cellist();
        
        musician.play(); // 피아노 연주 문자열 출력
        
    }
}

 

다형성에서 사용했던 예제를 그대로 가져왔다.

위 예제와 같이 상호의존도를 낮춰서 하나의 클래스의 변경이 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다. 

 


인터페이스의 다형성

인터페이스와 해당 인터페이스를 구현한 구현체의 관계에서 다형성이 성립된다.

 

public class Test {
    public static void main(String[] args) {
        Pianist kim = new Pianist();
        Musician musician = (Musician)kim;
    }
}​

인터페이스의 구현체는 인터페이스 타입으로 형변환이 가능하다.

 

public class Test {
    public static void main(String[] args) {
        InnerClass ic = new InnerClass();
        
        Pianist kim = new Pianist();
        // Violinist kim = new Violinist();
        // Cellist kim = new Cellist();
        
        ic.play(kim);
    }
}

class InnerClass {
    public void play(Musician musician) {
        musician.play():
    }
}

메소드의 매개변수에 인터페이스 타입을 지정할 수 있다.

 

public class Test {
    public static void main(String[] args) {
        InnerClass ic = new InnerClass();
        
        Musician kim = ic.musician();
    }
}

class InnerClass {
    public Musician musician() {
        return new Pianist();
        // return new Violinist();
        // return new Cellist();
    }
}

메소드의 리턴타입에 인터페이스 타입을 지정할 수 있다.

 


인터페이스 형식

인터페이스의 형식, 문법은 JAVA 8 이전과 이후로 구분할 수 있다.

 

JAVA 8 이전

public interface 인터페이스 {
    public String a = "abc";
    // public static final String a = "abc";
    
    public void a();
    // public abstract void a();
    
}

인터페이스에서 정의하는 변수는 모두 상수가 된다.

 

인터페이스에서 특정 접근제어자가 없는 메소드는 모두 추상 메소드가 된다.

 

JAVA 8 이후

public interface 인터페이스 {
    default 디폴트메소드() {
        ...
        디폴트메소드 로직;
        ...
    }
    
    static 정적메소드() {
        ...
        정적메소드 로직;
        ...
    }
}

디폴트메소드의 경우 제어자로 default를 명시해야한다.

디폴트메소드는 인터페이스에서 제공해주지만 추상 메소드와는 달리 무조건 구현체에서 오버라이딩을 하지 않아도 된다.

 

정적메소드의 경우 제어자로 static을 명시해야한다.

정적메소드는 인터페이스에서 제공해주며 인터페이스에서 정의한 내용을 그대로 사용해야한다.

 

인터페이스 문법을 이해하기 위한 예제는 다음과 같다.

 

public interface TestInterface {
	public String first = "TestInterface - first";
	public static final String second = "TestInterface - second";
	
	public void aMethod();
	
	public abstract void bMethod();
	
	default void cMethod() {
		System.out.println("TestInterface - cMethod");
	}
	
	static void dMethod() {
		System.out.println("TestInterface - dMethod");
	}

}
public class TestInterfaceImpl implements TestInterface {
	public String first = "TestInterfaceImpl - first";
	public String second = "TestInterfaceImpl - second";
	
	@Override
	public void aMethod() {
		System.out.println("aMethod override");
	}

	@Override
	public void bMethod() {
		System.out.println("bMethod override");
	}

	public void dMethod() {
		System.out.println("TestInterfaceImpl - dMethod");
	}
	
}

 

public class Test {

	public static void main(String[] args) {
		TestInterface testInterface = new TestInterfaceImpl();
		TestInterfaceImpl testInterfaceImpl = new TestInterfaceImpl();
		
		System.out.println(TestInterface.first);
		System.out.println(testInterface.first);
		System.out.println(testInterfaceImpl.first);
		
		System.out.println(TestInterface.second);
		System.out.println(testInterface.second);
		System.out.println(testInterfaceImpl.second);
		
		testInterface.aMethod();
		testInterfaceImpl.aMethod();
		
		testInterface.bMethod();
		testInterfaceImpl.bMethod();
		
		testInterface.cMethod();
		testInterfaceImpl.cMethod();
		
		TestInterface.dMethod();
		// testInterface.dMethod();
		testInterfaceImpl.dMethod();
	}

}

 

출력

TestInterface - first
TestInterface - first
TestInterfaceImpl - first
TestInterface - second
TestInterface - second
TestInterfaceImpl - second
aMethod override
aMethod override
bMethod override
bMethod override
TestInterface - cMethod
TestInterface - cMethod
TestInterface - dMethod
TestInterfaceImpl - dMethod

 

정적메소드의 경우 무조건 사용이라고 되어있어 구현체에서 해당 내용을 오버라이딩하되 인터페이스에서 정의한 정적메소드의 메소드 구현체와 인터페이스 구현체에서 오버라이딩한 정적메소드의 메소드 구현체가 서로 다를 시 문법적 오류가 발생할 줄 알았는데, 아예 오버라이딩의 대상이 되지 않는것을 확인할 수 있었다.

 

이는 static 제어자와 관련이 있어 보인다. 

 

그리고 상수는 TestInterface 타입의 TestInterfaceImpl 구현체에서도 호출이 가능했지만 정적메소드는 호출이 불가능했다.

 


정리

  • 인터페이스는 추상화와 다형성을 통해 객체간의 상호의존도를 낮추기 위한 기능이다.
  • 인터페이스는 동일한 목적 하에 동일한 기능을 강제하는 기능이다.
  • JAVA 8 이후 지원되는 디폴트 메소드를 통해 인터페이스 구현체에 대해 독립적인 메소드를 제공해 유연성을 확보해주었으며 정적메소드를 통해 필요한 경우 메소드의 선언부 뿐만 아니라 구현부 또한 강제할 수 있게 되었다.

해당 글은 개인이 공부하면서 정리한 글이기 때문에 정확하지 않은 내용이 있을 수 있습니다.

'정리 > JAVA' 카테고리의 다른 글

인터페이스와 추상클래스  (0) 2020.06.26
추상 클래스  (0) 2020.06.25
다형성  (0) 2020.05.27
상속  (0) 2020.05.25
추상화  (0) 2020.05.24