실제 코드는 작성하지 않더라도
어떤 메소드들이 있어야 하는지를 정의해 놓은 것
여러 개의 인터페이스를 구현할 수도 있음설계 시 선언해 두면 인터페이스 사용으로 로직을 먼저 구성하고 개발할 때 기능 구현에만 집중할 수 있음
-> 개발 시간 단축
클래스에 정형화된 틀을 제공하여 표준화가 가능함
다형성을 극대화하여 코드의 수정을 줄이고 유지보수성을 높일 수 있음
public 또는 defaultpublic static finalpublic interface BasicInterface {
// public static final 가 자동으로 적용됨
// + final 키워드가 붙으므로 반드시 선언 시 초기화 되어야 함
public static final int a = 10;
// public abstract 가 자동으로 적용됨
public abstract void basicMethod();
}
public class BasicClass implements BasicInterface, BasicInterface2 {
@Override
public void basicMethod() {
System.out.println("hi");
}
@Override
public void basic2() {
System.out.println("hihi");
}
}
인터페이스를 사용하면 인터페이스 레퍼런스를 써라!
인터페이스 타입의 변수인터페이스의 상속은 다중 상속이 가능함다른 인터페이스를 상속받은 인터페이스를 구현하는 클래스는 상위 인터페이스의 추상 메소드도 반드시 구현해야 함public interface FirstInterface {
void firstMethod();
}
public interface SecondInterface extends FirstInterface {
// 인터페이스끼리 상속은 상속받았다고 꼭 오버라이딩 해야하는 것은 아님
// @Override
// void firstMethod();
void secondMethod();
}
public class MultipleImpl implements SecondInterface{
// 상속받은 인터페이스를 구현하는 클래스는 구현하는 인터페이스의 부모 인터페이스도 구현해야 함
@Override
public void firstMethod() {
System.out.println("hi");
}
@Override
public void secondMethod() {
System.out.println("hihi");
}
void myMethod() {
System.out.println("hihihi");
}
public static void main(String[] args) {
System.out.println("----------클래스 레퍼런스----------");
MultipleImpl multiple = new MultipleImpl();
multiple.firstMethod();
multiple.secondMethod();
multiple.myMethod();
System.out.println("----------인터페이스 레퍼런스----------");
SecondInterface useInterface = new MultipleImpl();
useInterface.firstMethod();
useInterface.secondMethod();
//useInterface.myMethod(); -> useInterface 변수는 SecondInterface 타입이므로 myMethod 가 없음
}
}
[출력]
---------클래스 레퍼런스----------
hi
hihi
hihihi
----------인터페이스 레퍼런스----------
hi
hihi

각 인터페이스 별로 해당 클래스 객체를 생성해 사용할 수 있음
FirstInterface firstObj = new MultipleImpl(); // firstMethod()만 사용 가능
SecondInterface secondObj = new MultipleImpl(); // firstMethod(), secondMethod() 사용 가능
어떤 인터페이스의 메소드를 구현해야할지 구현 클래스에서 알 수 없으므로
에러 발생(다중 상속 불가능)
public interface InterfaceA {
String sayHi();
}
public interface InterfaceB {
void sayHi();
}
public class ImplAB implements InterfaceA, InterfaceB{
@Override
public String sayHi() { // 컴파일 에러 발생
return null;
}
}

중복되는 메소드를 구현할 수 있음
public interface InterfaceA {
String sayHi();
}
public interface InterfaceC {
String sayHi();
}
public class ImplAC implements InterfaceA, InterfaceC{
@Override
public String sayHi() {
return "hi";
}
public static void main(String[] args) {
ImplAC classReference = new ImplAC();
InterfaceA interfaceAReference = new ImplAC();
InterfaceC interfaceCReference = new ImplAC();
System.out.println(classReference.sayHi());
System.out.println(interfaceAReference.sayHi());
System.out.println(interfaceCReference.sayHi());
}
}
public interface Accelerator {
void accelerate();
}
public class Car {
private Accelerator accel;
public void setAccel(Accelerator accel) {
this.accel = accel;
}
}
public class OldAccel implements Accelerator {
@Override
public void accelerate() {
System.out.println("옛날 엑셀");
}
}
public class NewAccel implements Accelerator {
@Override
public void accelerate() {
System.out.println("최신 엑셀");
}
}
public class TightCoupling {
public static void main(String[] args) {
Car car = new Car();
Accelerator oldAccel = new OldAccel();
car.setAccel(oldAccel);
// 만약 newAccel 로 바꾸고 싶다면?
Accelerator newAccel = new NewAccel();
car.setAccel(newAccel);
}
}
public class Config {
public Accelerator accelerator() {
return new OldAccel();
// 만약 newAccel 로 바꾸고 싶다면?
// return new NewAccel();
}
}
public class LooseCoupling {
public static void main(String[] args) {
Car car = new Car();
Config config = new Config();
car.setAccel(config.accelerator());
// 만약 newAccel 로 바꾸고 싶다면?
// Accelerator 를 주입해주는 Config 클래스를 수정해주면 됨
}
}
⇒ 느슨한 결합을 사용하라!
인터페이스에서 추상 메소드 뿐만 아니라 구현된 메소드도 가질 수 있게 됨
오버라이딩 할 수 있음인터페이스를 구현한 클래스에 영향을 주지 않으면서 기능을 추가하기 위해 사용
추상 메소드를 추가하면?default method를 추가public interface UsedInterface {
void hi();
// bye 기능을 추가해야함
// void bye(); -> 인터페이스를 구현한 구현체에서 에러가 남 (추가된 추상 메소드가 구현되지 않았으므로)
default void bye() { // 구현체에서 에러가 안남 -> default method는 오버라이딩하지 않아도 되므로
System.out.println("bye");
}
}
public class OnlyHi implements UsedInterface {
@Override
public void hi() {
System.out.println("hi");
}
}
public class HiAndBye implements UsedInterface{
@Override
public void hi() {
System.out.println("hi");
}
@Override
public void bye() {
System.out.print("good");
UsedInterface.super.bye();
}
}
인터페이스의 default 메소드 사용
인터페이스명.super.메소드명()인스턴스로 메소드를 호출할 수 없음인터페이스.메소드명()으로 호출해야 함오버라이딩 할 수 없음해당 인터페이스를 구현한 모든 클래스에서 동일한 메소드를 사용하기 위해 사용
public interface UsedInterface {
...
static void staticMethod() {
System.out.println("모든 구현체에서 이 메소드를 사용해라");
}
}
public class UseInterfaceClass {
public static void main(String[] args) {
OnlyHi impl1 = new OnlyHi();
HiAndBye impl2 = new HiAndBye();
impl1.hi();
impl2.hi();
impl2.bye();
// impl1.staticMethod(); -> 구현 객체를 통해 static method를 호출할 수 없음
UsedInterface.staticMethod();
}
}
이전 버전의 자바 코드에서는 호환되지 않을 수 있음인터페이스의 필드는 상수로만 가질 수 있기 때문에 구현체에서 같은 이름의 다른 값을 갖는 필드를 제공해 줄 수 없음필드는 상수만 가지면서 메소드(행동) 위주의 상속을 원하는 경우 인터페이스를 쓰는게 좋겠다.
클래스마다 같은 이름, 다른 값을 갖는 필드가 존재해야 한다면 여전히 추상 클래스를 사용해야 한다.
내부에서만 사용될 메소드를 private으로 만들어 쓸 수 있음private 메소드가 외부로 공개되는 것을 방지해야 함public interface UsedInterface {
void hi();
// bye 기능을 추가해야함
// void bye(); -> 인터페이스를 구현한 구현체에서 에러가 남 (추가된 추상 메소드가 구현되지 않았으므로)
default void bye(int count) { // 구현체에서 에러가 안남 -> default method 오버라이딩하지 않아도 되므로
System.out.println("start bye");
validCheckAndRepeat(count); // 내부에 복잡한 로직이 있다면 private 메소드를 만들어서 분리할 수 있음
System.out.println("end bye");
}
private void validCheckAndRepeat(int count) {
if (count < 10) {
for (int i = 0; i < count; i++) {
System.out.println("bye");
}
}
}
static void staticMethod() {
System.out.println("모든 구현체에서 이 메소드를 사용해라");
}
}
인터페이스 상속