추상클래스와 인터페이스는 깊은 연관성이 있다. 인터페이스도 일종의 추상클래스이기 때문이다.
인터페이스는 무엇인지, 또 추상클래스와 어떤 다른 어떤 특징을 가지는지 알아보자.
추상클래스의 일종이지만, 추상클래스보다 추상도 즉, ‘강제성’이 더 강하다고 보면된다.
추상클래스와 다르게 인터페이스는 일반메소드, 멤버변수를 가질 수 없다.
오로지 추상메소드 혹은 상수만 가질 수 있다. 그외에 어떤 요소도 허용되지 않는다.
인터페이스를 작성하는건 어렵지 않다.
interface Inter {
//1
public final int a = 1;
public abstract void play();
//2
int b = 1;
void plaing();
//3
default void plaingNumber() {
}
}
인터페이스는 interface 란, 용어를 사용해 인터페이스를 선언할 수 있다.
단, 접근제어자로 public default 만 사용할 수 있다.
모든 멤버변수는 public static final 이어야하며, 생략가능하다.
메서드는 public abstract 이어야하며, 생략가능하다.
(JDK 1.8 이상에서 static와 default 메소드는 예외.) → 추상 메소드가 아니어도, 선언이 가능.
인터페이스는 인터페이스간 상속만 가능하다.
단, 일반클래스와 다르게 ‘다중상속’이 가능하다.
interface Inter extends Player, Listener{
}
interface Player {}
interface Listener {}
단계적으로 인터페이스 구현에 대해 알아보자.
class InterTest implements Inter {}
인터페이스 또한 추상클래스처럼 인스턴스를 생성할 수 없는 것은 물론 추상클래스가 상속을 위해 extends 를 사용했다면, 인터페이스는 implements 라고 선언해줘야한다.
물론, 추상클래스와 동일하게 ‘미완성 메소드’ 들은 모두 명시해야하는 ‘강제성’을 띈다.
interface Inter extends CDPlayer, Listener{
default void pass () {
}
static void pass2 () {
}
}
interface CDPlayer {
void musicIng();
}
interface Listener {
void musicStart();
void musicStop();
void singing();
}
조금의 코드를 추가해보자. CDPlayer Listener 인터페이스에 추상메소드를 각각 작성해주었다.
Inter 라는 이름의 인터페이스는 CDPlayer Listener 인터페이스를 모두 상속받고, 중요한 점이 몇 가지 존재한다.
extends 를 사용한다.default static 메소드 생성이 가능하며, ‘강제성이 없는' 일반 메소드 활용이 가능하다. (단, JDK 1.8 이상에서 가능하며, 인터페이스를 활용할 경우에는 항상 메소드 간 충돌을 주의해야한다.)class InterTest implements Inter {
@Override
public void musicIng() {
}
@Override
public void musicStart() {
}
@Override
public void musicStop() {
}
@Override
public void singing() {
}
}
이제 일반 클래스에 인터페이스를 구현할 InterTest 라는 ‘구현체’를 작성해보자.
implements 를 활용해 일반 클래스에 상속할 수 있다. 엄연히, implements 와 extends 는 다르다. 그리고 둘은 동시에 사용할 수 있다는 점도 알아야한다.
InterTest 구현체에 상속받을 임의의 클래스를 하나 생성해 InterTest 클래스와 상속해보자.
class InterTest extends InterTestExtension implements Inter {
@Override
public void musicIng() {
}
@Override
public void musicStart() {
}
@Override
public void musicStop() {
}
@Override
public void singing() {
}
@Override
void add() {
super.add();
}
@Override
void delete() {
super.delete();
}
}
class InterTestExtension {
void add () {}
void delete () {}
}
임의의 InterTestExtension 이라는 클래스를 생성해, InterTest 에 extends 를 활용해 상속했다.
클래스에 **<span style="color:rgb(165, 102, 255)">extends 를 활용한 ‘상속’ 과 implements 를 활용한 ‘구현’은 동시에 가능하다는 점**을 기억해야한다. (당연히, 추상클래스로 선언해 상속도 가능할 것이다.)
우리는 다형성을 활용해 자손클래스의 인스턴스를 조상타입의 참조변수로 참조할 수 있다.
인터페이스 타입의 참조변수를 구현한 클래스의 인스턴스를 참조할 수 있고, 인터페이스로의 형변환도 가능하다.
예를들어 다음과 같다.
void methodInter(Inter a) { //1
}
Inter methodInterTwo() { //2
return new InterTest();
}
인터페이스를 참조변수로 사용할 수 있다.
메소드 리턴타입을 인터페이스로 지정할 수 있고, 그에 따라 ‘구현체’인 InterTest() 를 리턴할 수도 있다.
⇒ 구현체가 구현한 메소드 정보를 활용할 수 있단 뜻.
개발시간 단축
→ 인터페이스는 선언부만 작성 후, 이후 내용들은 각 구현체에서 구현되기 때문에 독립적이고, 다수의 작업을 동시에 진행할 수 있다.
추상화를 통한 표준화
→ 추상클래스보다 강화된 ‘강제성’을 활용해 특정 공통기능을 구현하는 클래스들이 인터페이스를 활용함으로써 조금 더 획일화된 프로그래밍을 진행할 수 있다.
클래스들간의 관계성 확립가능
→ 시스템이 복잡해지면 수 십개 혹은 수 백개의 클래스가 생성될 수 있는데, 인터페이스를 활용하면 어떤 클래스가 어떤 기능을 지녀야하고 지니고 있는지 파악하기 쉬워지고 그에 따른 관계도 파악하기 쉽다.