이 글은 백기선님의 라이브 스터디 참여 및 학습 내용에 관한 정리한 글입니다.
인터페이스(interface)란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하며, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다고 한다.
선언 방법
1. 인터페이스 선언시 interface
키워드를 사용한다.
2. 인터페이스의 모든 메소드는 public abstract 모든 필드는 public static final 이어야 한다. (단, 자바8 이후에는 static 메소드, default 메소드는 예외)
3. 위 키워드들은 개발자가 생략을 해도 되며, 컴파일러가 자동으로 해당 키워드를 추가해준다.
선언 예시
interface InterfaceTest { // 'interface' 키워드를 통해 선언
public static fianl NUMBER = 1;
public abstract testRun(); // 구현부가 없는 추상 메소드
}
인터페이스 사용시 장점
인터페이스 또한 추상클래스와 마찬가지고 자신이 직접 인스턴스를 생성할 수 없다. 인터페이스가 포함하고 있는 추상 메소드를 구현화할 클래스가 필요한 것이다. 자바에서 클래스가 인터페이스를 구현화할 때에는 implements
키워드를 사용하면 된다. 클래스 상속에서 사용하던 extends
키워드와는 다르며, 인터페이스 구현화는 복수로 구현화가 가능하다.
class Test implements InterfaceName {
}
그런데 인터페이스 정의에서 의문점이 한가지 있는데 바로 추상 클래스라는 용어다. 자바에서는 추상 클래스가 있는데 인터페이스도 추상 클래스와 비슷하다고 하니 이해가 가질않는다.
자바에서 추상 클래스는 추상 메소드, 필드, 생성자, 일반 메소드를 포함하는 것이 가능하다. 그러나 인터페이스는 오직 추상 메소드, 상수만을 가질 수 있다. (여기서 자바8 이후 부터는 default 메소드와 static 메소드가 추가됨)
여기서 위와 같은 선언 방식에 대한 차이가 있는 것은 알겠으나 개발자가 이 비슷한 두 가지를 어떤 상황에 활용하는 것이 좋을지에 대한 의미 구분이 모호한 것 같다. 그러던 중 인터페이스와 추상 클래스의 역할의 차이를 구분에 대한 내용을 찾아보면서 다른 블로그의 내용이 좋아 참고했습니다.
- 참고 블로그 : https://myjamong.tistory.com/150
추상 클래스는 특정 공통의 객체를 추상화할 때 사용하는 것이 좋고 인터페이스는 해당 객체의 행위에 대해서 추상화할 때 사용하는 것이 좋다.
예시) 다음은 아래 텍스트를 코드로 구현화 해본 것 입니다.
개발자는 사람(공통 객체)이다.
개발자는 코딩(행위)을 할 수 있다.
class Person {
String name;
int age;
}
interface Development {
public abstact void coding();
}
public class Developer extends Person implements Development {
String language;
public Developer(String name, int age, String language) {
super(name, age);
this.language = language;
}
@Override
public void coding() {
System.out.println(language + " 개발자는 " + language + "를 코딩중이다.");
}
}
자바는 인터페이스를 통해 다형성을 이용할 수 있다. A라는 클래스에서 B라는 인터페이스를 구현화한 객체라면 B 타입의 참조변수에 A 클래스의 객체를 참조할 수 있다.
interface B {
void run();
}
class A implements B {
@Override
public void run() {
System.out.println("running..");
}
}
class Main {
public static void main(String[] args) {
B parent = new A(); // B 라는 인터페이스 타입에 A 객체를 주입
parent.run();
}
}
인터페이스 상속에서는 클래스와 다르게 다중 상속이 가능하다. 클래스에서는 부모 클래스의 메소드가 자손에서 충돌나는 문제를 방지하기 위해서인데, 인터페이스에서는 존재하는 추상 메소드는 선언부만 존재하기 때문에 충돌날 가능성이 없기 때문에 다중 상속을 할 수 있다.
그러나, 다중 상속할 시 상위 인터페이스에 있는 메소들 중에서 메소드명, 매개변수 형식이 같으나 리턴타입은 서로 다른 메소드가 있다면 이 경우에는 다중 상속이 불가하여 컴파일 에러를 발생시킨다.
위와 같이 C라는 인터페이스가 A와 B 인터페이스를 상속 받을 때, A와 B는 run() 이라는 메소드명이 같고 매개변수 형식도 동일한 메소드가 있다. 이때 run() 메소드에서 충돌이 일어나면서 컴파일 에러가 발생하게 되는 것이다.
자바 8부터는 인터페이스에 기본 메소드(default method)가 추가되었다. 이 메소드가 추가된 배경을 다음과 같다. A라는 인터페이스를 구현화하고 있는 클래스들은 A의 추상 메소드들을 모두 구현화해야한다 그런데 만약에 A 인터페이스에 새로운 메소드가 추가되었다고 생각해보자 그러면 이 A를 구현화하는 모든 클래스들에서 해당 메소드를 구현화하는 작업이 필요한 것이다. 때문에 인터페이스에 새로운 메소드를 추가한다는 것은 굉장히 부담스러운 일이다. 이를 보완하기 위해 나타난 것이 기본 메소드이며 기존 인터페이스에서 선언하던 추상 메소드와 다르게 default
키워드를 앞에 붙여서 사용하여 선언하며 일반 메소드처럼 구현부가 있어야 한다.
사용 예시
public interface Foo {
default void print() {
System.out.println("default method!");
}
}
주의사항
자바8 부터 static 메소드가 추가 되었다. 재정의(오버라이딩)이 가능하던 기본 메소드와는 다르게 static 메소드는 오버라이딩이 불가하며 객체의 인스턴스화 없이도 해당 메소드 사용이 가능하다.
interface Test {
static void running(String name) {
System.out.println(name + "을 실행중..");
}
}
자바9 부터 인터페이스에 private 메소드와 private static 메소드가 추가되었다.
추가된 배경
private 메소드 규칙
사용 예시
interface Tester {
private void run() {
System.out.println("private 메소드!");
}
private static void running() {
run();
System.out.println("private static 메소드");
}
}
강한 결합과 느슨한 결합에 대한 비교, 아직 이 내용이 이해가 명확하게 되지는 않아서 자주 보고 확실히 짚고 넘어가야 겠다.
참고 블로그