학습할 것

  • 인터페이스 정의하는 방법
  • 인터페이스 구현하는 방법
  • 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
  • 인터페이스 상속
  • 인터페이스의 기본 메소드 (Default Method), 자바 8
  • 인터페이스의 static 메소드, 자바 8
  • 인터페이스의 private 메소드, 자바 9

인터페이스


인터페이스는 자바의 다형성을 극대화하여 객체지향프로그래밍을 더 수월하게 해주는 역할을 합니다.

인터페이스를 통해 객체는 추상화에 더 의존하게 되고, 이에 따라 프로그램의 유지 보수가 용이합니다.

인터페이스 정의


인터페이스의 구성요소는 크게 상수필드, Abstract 메서드, Default 메서드, Static 메서드, Private 메서드가 있습니다.

  • 상수 필드 : 인터페이스에서 정의한 상수를 클래스에서 그대로 값만 참조하여 사용할 수 있게 합니다.
  • Abstract 메서드 : 인터페이스 구현 클래스에서 직접 오버라이딩 해서 구현해야 하는 메서드(빈 껍데기)를 말합니다.
  • Default 메서드 : 인터페이스에서 기본적인 메서드 내용을 정의해주지만, 구현 클래스마다 이를 오버라이딩해서 재정의 할 수 있습니다.
  • Static 메서드 : 구현 클래스에서 변경할 수 없는(오버라이딩 할 수 없는) 인터페이스가 정한 메서드를 말합니다.
  • Private 메서드 : 아래에서 살펴보도록 하겠습니다.

인터페이스의 구현


인터페이스는 추상 클래스와 마찬가지로 자신이 직접 인스턴스를 생성할 수는 없습니다.
따라서 인터페이스가 포함하고 있는 추상 메소드를 구현해 줄 클래스를 작성해야만 합니다.

자바에서 인터페이스는 다음과 같은 문법을 통해 구현합니다.

문법

class 클래스이름 implements 인터페이스이름 {...}

만약 모든 추상 메소드를 구현하지 않는다면, abstract 키워드를 사용하여 추상 클래스로 선언해야 합니다.

다음 예제는 인터페이스를 구현하는 예제입니다.

Animal.java

interface Animal {
	public abstract void cry();
}

Cat.java

class Cat implements Animal {
	public void cry() {
		System.out.println("냐옹");
	}
} 

Dog.java

class Dog implements Animal {
	public void cry() {
		System.out.println("멍멍");
	}
}

Polymorphism03.java

public class Polymorphism03 {
	public static void main(String[] args) {
		Cat c = new Cat();
		Dog d = new Dog();
		c.cry();
		d.cry();
	}
}

실행 결과

냐옹
멍멍

인터페이스 레퍼런스를 통해 구현체를 사용하는 방법


Animal.java

interface Animal {
	public abstract void cry();
}

Cat.java

class Cat implements Animal {
	public void cry() {
		System.out.println("냐옹");
	}
    public void name() {
		System.out.println("Milk");
	}
}

Dog.java

class Dog implements Animal {
	public void cry() {
		System.out.println("멍멍");
	}
    public void name() {
		System.out.println("cream");
	}
}

Polymorphism03.java

public class Polymorphism03 {
	public static void main(String[] args) {
		Cat c = new Cat();
		Dog d = new Dog();
		c.cry();
		d.cry();
        ((Cat)c).name();
		((Dog)d).name();
	}
}

실행 결과

냐옹
멍멍
Milk
cream

인터페이스는 구현체를 통해서 인스턴스 생성이 가능합니다.
dog.name()이 사용 못하는건 캐스팅을 안해줘서 사용을 못합니다. dog가 바라보는 것은 Animal 인터페이스입니다.
사용하려면 ((Dog)d).name() 으로 캐스팅하여 사용이 가능합니다.

인터페이스 상속


자바는 다중 상속이 불가능한데 인터페이스는 다중 구현이 가능합니다.
인터페이스는 인터페이스부터만 상속을 받을 수 있습니다.

public interface extendsInterface extends Animal { }

상속과 구현을 동시에 할 수 있습니다.

class 클래스명 extends 상위클래스명 implements 인터페이스명 {...}

여러 인터페이스를 구현할 수 있습니다.

public class imploSample implements Animal, ITest {
	@Override
    public void cry() {}
    
    @Override
    public void abstractMethod() {}
    
    @Override
    public void nonAbstractMethod() {}
}

자바 8, Default와 Static 메소드


Default Method

Java에서 기존의 interface는 추상 메소드만을 가질 수 있었습니다. 그런데 Java8부터 default 키워드를 사용해서 interface에 메소드를 선언할 수 있게 되었습니다.

public interface ICalculator {
	int add(int x, int y);
	int sub(int x, int y);
	
	default int mul(int x, int y) {
		return x * y;
	}
}

메소드를 default 키워드를 사용해 선언함으로써 메소드 body, 즉 구현부를 작성할 수 있게 되었습니다.

public class Calculator implements ICalculator {
	@Override
	public int add(int x, int y) {
		return x + y;
	}
	
	@Override
	public int sub(int x, int y) {
		return x - y;
	}
}

해당 interface를 구현하는 class에서는, default 키워드로 구현하지 않은 add, sub 메소드만 구현하면 컴파일 에러가 발생하지 않습니다.

public class CalcTest {
	public static void main(String[] args) {
		ICalculator cal = new Calculator();
		int result = cal.mul(5, 3);
		System.out.println("5 * 3 = "+result);
	}
}

Calculator class에서 mul 메소드를 구현하지 않았지만 ICalculator interface에 default 키워드로 구현이 되어있기 때문에 호출해서 사용할 수 있습니다.

실행결과

5 * 3 = 15
public class Calculator implements ICalculator {
	@Override
	public int add(int x, int y) {
		return x + y;
	}
	
	@Override
	public int sub(int x, int y) {
		return x - y;
	}
	
	public int mul(int x, int y) {
		System.out.println(x + "와 " + y + "를 곱합니다.");
		return x + y;
	}
}

물론 필요 시, 이렇게 default 키워드로 구현된 메소드를 overriding 할 수 있습니다.

실행결과

5와 3를 곱합니다.
5 * 3 = 8

Static Method

public interface ICalculator {
	int add(int x, int y);
	int sub(int x, int y);
	
	default int mul(int x, int y) {
		return x * y;
	}
	
	static void print(int value) {
		System.out.println(value);
	}
}

또한 Java8부터 interface에 static 메소드를 선언할 수 있게 되었습니다. 위 ICalculator interface의 print 메소드가 그 예에 해당합니다.

public class CalcTest {
	public static void main(String[] args) {
		ICalculator cal = new Calculator();
		ICalculator.print(100);
	}
}

단, static 메소드를 사용하는데 주의해야 할 점은 기존 클래스의 static 메소드처럼 class이름.메소드로 호출하는게 아니라 interface이름.메소드로 호출해야 한다는 것입니다.

실행결과

100

자바 9, Private Method


자바9부터는 private method를 사용할 수 있게 되어서 외부에서 접근할 수 없도록 만드는 게 가능해졌습니다.

Calculator

public interface Calculator {
	private String printBye() {
    	return "Bye";
    }
    default void knockDoor() {
    	System.out.println("OK..." + printBye());
    }
}

CalcTest

public class CalcTest implements Calculator {
	@Override
	public String printBye() {
    	return "Hello!"; 👈 Error!
    }
}

'Calculator' 인터페이스에서 printBye()는 private메서드입니다. 따라서 구현 클래스가 printBye()를 오버라이딩하는 것 자체가 불가능합니다.

하지만, 인터페이스에서 정의한 printBye()는 디폴트 메서드인 knockDoor()에서 사용됨을 볼 수 있습니다.

즉, private메서드를 인터페이스 안에 생성해두고, 디폴트 메서드나 정적 메서드 안에서 이를 사용하면 코드의 수정과 유지보수가 쉬워지고 재사용성이 올라간다는 뜻입니다.

만약 private메서드가 없는 상태에서 디폴트 메서드가 5개, 정적 메서드가 5개라고 해봅시다. 그런데 이 10개의 메서드에서는 동일한 작업을 해야 하는 로직이 포함됩니다. 그러면 이 10개의 메서드에 이 로직을 일일이 쳐야할까요? 매우 고통스러울 것입니다. 이게 바로 private 메서드의 필요성입니다.

출처

http://www.tcpschool.com/java/java_polymorphism_interface
https://sujl95.tistory.com/60
https://atoz-develop.tistory.com/entry/JAVA-8-interface-default-%ED%82%A4%EC%9B%8C%EB%93%9C%EC%99%80-static-%EB%A9%94%EC%86%8C%EB%93%9C
https://studyandwrite.tistory.com/96

0개의 댓글