인터페이스는 기본적으로 추상 메서드와 상수만을 멤버로 가질 수 있다는 점에서 추상 클래스에 비해 추상화 정도가 더 높다.
❗️인터페이스는 "추상 메서드"의 집합
public interface InterfaceEx {
public static final int rock = 1; // 인터페이스 인스턴스 변수 정의
final int scissors = 2; // public static 생략
static int paper = 3; // public final 생략
public abstract String getPlayingNum();
void call() // public abstract 생략
}
위 예시 코드를 보면 추상 메서드와 상수만으로 구현되어 있다.
인터페이스 안에서 상수를 정의하는 경우에 반드시 "public static final"로
메서드를 정의하는 경우 "public abstract"로 정의해야하지만
일부분 또는 전부 생략이 가능하기도 하다.
❗️인터페이스 자체의 인스턴스 생성 불가
**implements"" 키워드를 사용한다. '구현하다'란 의미를 가지고 있다.
class 클래스명 implements 인터페이스명 {
... // 인터페이스에 정의된 모든 추상메서드 구현
}
특정 인터페이스를 구현한 클래스는 반드시 해당 인터페이스에 정의된 모든 추상메서드를 구현해야된다.
특정 인터페이스를 구현한다 = 해당 클래스에게 인터페이스의 추상 메서드를 반드시 구현하도록 강제한다.
다른 말로, 인터페이스가 가진 모든 추상 메서드들을 해당 클래스 내에서 오버라이딩하여 바디를 완성한다.
상속은 다중 상속이 허용되지 않지만 인터페이스는 다중 구현이 가능하다.
class ExampleClass implements ExampleInterface1, ExampleInterface2, ExampleInterface3 {
... 생략 ...
}
interface Animal { // 인터페이스 선언. public abstract 생략 가능.
public abstract void cry();
}
interface Pet {
void play();
}
class Dog implements Animal, Pet { // Animal과 Pet 인터페이스 다중 구현
public void cry(){ // 메서드 오버라이딩
System.out.println("멍멍!");
}
public void play(){ // 메서드 오버라이딩
System.out.println("원반 던지기");
}
}
class Cat implements Animal, Pet { // Animal과 Pet 인터페이스 다중 구현
public void cry(){
System.out.println("야옹~!");
}
public void play(){
System.out.println("쥐 잡기");
}
}
public class MultiInheritance {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.cry();
dog.play();
cat.cry();
cat.play();
}
}
// 출력값
멍멍!
원반 던지기
야옹~!
쥐 잡기
위 예제 코드에서 확인하듯, Dog와 Cat 클래스는 각각 Animal과 Pet 인터페이스를 다중으로 구현하고 각각의 객체에 맞는 메서드를 오버라이딩한다.
🟩특정 클래스는 다른 클래스로부터 상속을 받으면서 동시에 인터페이스 구현이 가능하다.
abstract class Animal { // 추상 클래스
public abstract void cry();
}
interface Pet { // 인터페이스
public abstract void play();
}
class Dog extends Animal implements Pet { // Animal 클래스 상속 & Pet 인터페이스 구현
public void cry(){
System.out.println("멍멍!");
}
public void play(){
System.out.println("원반 던지기");
}
}
class Cat extends Animal implements Pet { // Animal 클래스 상속 & Pet 인터페이스 구현
public void cry(){
System.out.println("야옹~!");
}
public void play(){
System.out.println("쥐 잡기");
}
}
public class MultiInheritance {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.cry();
dog.play();
cat.cry();
cat.play();
}
}
// 출력값
멍멍!
원반 던지기
야옹~!
쥐 잡기
의존한다 : user 클래스에서 provider 에 정의된 특정 속성 또는 기능을 가져와 사용한다.
public class InterfaceExample {
public static void main(String[] args) {
User user = new User(); // User 클래스 객체 생성
user.callProvider(new Provider()); // Provider 객체 생성 후에 매개변수로 전달
}
}
class User { // User 클래스
public void callProvider(Provider provider) { // Provider 객체를 매개변수로 받는 callProvider 메서드
provider.call();
}
}
class Provider { //Provider 클래스
public void call() {
System.out.println("무야호~");
}
}
// 출력값
무야호~
위 예제 코드를 보면 User 클래스에 정의된 callProvider 메서드의 매개변수로 provider 타입이 전달되어 호출되고 있는데...
만약 User클래스가 의존하고 있는 Provider 클래스에 변경사항이 발생해서 Provider 클래스가 아닌 Provider2 클래스로 교체해야 한다면?
public class InterfaceExample {
public static void main(String[] args) {
User user = new User(); // User 클래스 객체 생성
user.callProvider(new Provider2()); // Provider객체 생성 후에 매개변수로 전달
}
}
class User { // User 클래스
public void callProvider(Provider2 provider) { // Provider 객체를 매개변수로 받는 callProvider 메서드
provider.call();
}
}
class Provider2 { //Provider 클래스
public void call() {
System.out.println("야호~");
}
}
// 출력값
야호~
provider 클래스에 의존하고 있는 User 클래스의 코드의 변경이 불가피하다.. 불편하다..
만약 방대한 숫자의 코드라면 변경해야할 부분이 수백 수천가지일 것이다.
❤️이때, 인터페이스의 큰 장점 중 하나, 인터페이스의 기능처럼 역할과 구현을 분리시켜서 사용자 입장에서는 복잡한 구현의 내용 또는 변경과 상관 없이 기능을 사용할 수 있다는 점이다.
Provider 클래스에 인터페이스를 씌어주면 User 클래스는 더이상 Provider 의 교체 또는 변경에 상관없이 인터페이스와의 상호작용을 통해 의도한 목적을 달성한다.
public class Interface4 {
public static void main(String[] args) {
User user = new User();
user.callProvider(new Provider2());
user.callProvider(new Provider());
}
}
class User {
public void callProvider(Cover cover) { // 매개변수의 다형성 활용
cover.call();
}
}
class Provider implements Cover {
public void call() {
System.out.println("무야호~");
}
}
class Provider2 implements Cover {
public void call() {
System.out.println("야호~");
}
}
//출력값
야호~
무야호~
위 예제 코드를 보면 cover 라는 인터페이스를 정의한 후에 각각 구현체에 implements 키워드를 상하여 각각 기능을 구현하고 있다.
User 클래스에는 매개변수의 다형성을 활용하여 구체적인 구현체가 아닌 인터페이스를 매개변수로 받고 있다.
이에따라 Provider 클래스의 내용 변경 또는 교체가 발성하더라도 User 클래스는 더이상 코드를 변경해주지 않아도 같은 결과를 출력한다.
📢정리를 해보자면