💡 인터페이스 : 표준, 강제, 클래스간 유사성
인터페이스는 일종의 추상클래스이며, 추상메서드와 상수만을 포함하며 다른 클래스를 작성하는데 도움 줄 목적으로 작성된다.
키워드로 class
대신 interface
를 사용한다. 접근제어자로 public
, default
도 사용 가능하다.
인터페이스 이름에는 주로 ~able
로 끝나는 것들이 많다. 이는 어떠한 기능 또는 행위를 하는데 필요한 메서드를 제공한다는 의미를 강조한다.
interface 인터페이스이름 {
(public static final) 타입 상수이름 = 값; //값이 대입되어야 한다.
(public abstract) 메서드이름(매개변수);
}
public static final
이어야 하며, 이를 생략할 수 있다.public abstract
여야 하며, 이를 생략할 수 있다.인터페이스는 인터페이스를 상속 받을 수 있으며, 여러 인터페이스를 상속받는 다중 상속이 가능하다.
인터페이스도 참조변수 선언은 가능하지만 인스턴트 생성을 할 수 없다. 인터페이스는 자신에 정의된 추상메서드를 구현할 클래스가 필요하며, 이 클래스는 구현한다는 의미의 키워드 implements
를 사용한다.
class 클래스이름 implements 인터페이스이름 {
// 인터페이스에 정의된 추상메서드를 구현해야 한다.
}
//상속과 구현을 같이 할 수도 있다
class 클래스이름 extends 부모클래스이름 implements 인터페이스이름 {
// 인터페이스에 정의된 추상메서드를 구현해야 한다.
}
오버라이딩할 때는 조상의 메서드보다 넓은 범위의 접근 제어자를 지정해야 한다. 모든 인터페이스의 메서드는 public abstract
이므로 구현하는 클래스의 메서드의 접근제어자는 반드시 public
이 되어야 한다.
'자바도 인터페이스를 이용하면 다중상속이 가능하다'라고 하는 것일 뿐 자바에서 인터페이스로 다중상속을 구현하는 경우는 거의 없다.
인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
Fightable f = (Fightable) new Fighter ();
Fightable f = new Fighter();
그리고 함수의 매개변수 또는 리턴 타입으로 인터페이스 타입을 사용할 수도 있다.
interface Fightable {
...
void attack (Fightable f);
}
class FighterManager {
public static Fightable getFighter (String type) {
if (type.equals("lv1")) {
Fighter f = new Fighter();
return f; // = return new Fighter();
} else {
Boss b = new Boss();
return b; // = return new Boss();
}
}
}
class Fighter implements Fightable {
...
public void attack (Fightable f) {
/* 공격하는 코드 */
System.out.println("공격");
}
}
class Boss implements Fightable {
...
public void attack (Fightable f) {
/* 공격 세게하는 코드 */
System.out.println("세게 공격");
}
}
💡 리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.
Fightable 인터페이스는 공격 기능 구현을 목적으로 attack()을 정의했다. 그리고 Fighter와 Boss 클래스에서 인터페이스를 구현하였다.
FightManager의 getFigher를 이용해 매개변수의 값에 따라 Fighter 또는 Boss의 인스턴스를 반환받을 수 있다. 즉, 필요에 따라 같은 목적의 다른 기능을 하는 인스턴스를 사용할 수 있다.
public class InterfacePrac {
public static void main(String[] args) {
Fightable a = new Fighter();
Fightable f = FighterManager.getFighter("lv1");
f.attack(a);
Fightable g = FighterManager.getFighter("lv100");
g.attack(a);
}
}
//result
공격
세게 공격
만일 나중에 새로운 Fighter가 나오면 main 메서드 변경없이 getFighter만 변경하면 된다.
이러한 장점은 특히 분산환경 프로그래밍에서 그 위력을 발휘한다. 사용자 컴퓨터에 설치된 프로그램을 변경하지 않고 서버측의 변경만으로도 사용자가 새로 개정된 프로그램을 사용하는 것이 가능하다.
1. 개발 시간을 단축시킬 수 있다.
일단 인터페이스가 작성되면
1) 메서드를 호출하는 쪽에서는, 메서드의 내용에 관계없이 선언부만 알면된다.
2) 인터페이스를 구현하는 클래스를 작성하는 쪽에서는, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도
양쪽에서 동시에 개발을 진행할 수 있다.
2. 표준화가 가능하다.
프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.
3. 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.
서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어줄 수 있다.
Ex) Repairable
4. 독립적인 프로그래밍이 가능하다.
인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다. 클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.
Ex) 데이터베이스 관련 인터페이스 : 스프링 게시판 구현에서 repository interface를 작성하고, 데이터 접근 방법을 바꿔낄 수 있게 한다.
💡 인터페이스 이해
- 클래스를 사용하는 쪽 (User)과 클래스를 제공하는 쪽 (Provider)이 있다.
- 메서드를 사용 (호출)하는 쪽 (User)에서는 사용하려는 메서드 (Provider)의 선언부만 알면 된다. (자세한 코드는 몰라도 된다.)
JDK 1.8부터는 디폴트 메서드와 static 메서드도 추가할 수 있게 되었다.
java.util.Collection 인터페이스가 있는데 이 인터페이스와 관련된 static 메서드들이 인터페이스에는 추상 메서드만 선언할 수 있다는 원칙 때문에 별도의 클래스, Collections라는 클래스에 들어가게 되었다. 만일 인터페이스에 static 메서드를 추가할 수 있었다면, Collections 클래스는 존재하지 않았을 것이다.