[그린컴퓨터] Server/JAVA(객체 지향 프로그래밍)

인터페이스 활용하기 { 한 클래스가 여러 인터페이스를 구현, 두 인터페이스의 디폴트 메서드가 중복되는 경우, 인터페이스 상속하기, 실무에서 인터페이스를 사용하는 경우, JDBC와 인터페이..

Ben의 프로그램 2023. 6. 5. 11:17
728x90

한 클래스가 여러 인터페이스를 구현하는 경우

  • 한 클래스가 여러 클래스를 상속받으면 메서드 호출이 모호해지는 문제가 발생할 수 있습니다. 
  • 하지만 인터페이스의 구현은 상속과는 다르게 한 클래스가 여러 인터페이스를 구현할 수 있습니다. 

  • 위 그림을 보면  Customer 클래스는 Buy 와 Sell 두 인터페이스를 구현합니다.
  • 이를 코드로 나타내면 다음과 같습니다.
public interface Buy {
	void buy();
}

 

public interface Sell {
	void sell();
}

 

public class Customer implements Buy, Sell {

	@Override
	public void sell() {
		System.out.println("판매하기");
	}

	@Override
	public void buy() {
		System.out.println("구매하기");
	}

}
  • 인터페이스는 구현 코드나 멤버 변수를 가지지 않기 때문에 여러 개를 동시에 구현할 수 있습니다. 
  • 이게 무슨 말이냐면, 인터페이스를 구현하는 클래스에서 어차피 추상 메서드를 모두 구현해야 하기 때문에 구현하는 인터페이스들에 동일한 이름의 추상 메서드가 있어봤자 의미가 없다는 것을 의미합니다. 
  • 결국 구현하는 두 인터페이스에서 이름이 같은 메서드가 선언되었다고 해도 구현은 클래스에서 이루어지기 때문에, 어떤 메서드를 호출해야 하는지 모호하지 않습니다. 
  • 위 코드에서 Sell 인터페이스와 Buy 인터페이스를 구현한 Customer 클래스는 Buy 형이자 Sell 형이기도 합니다. 
  • 이런 특성을 보여주는 다음과 같은 코드를 확인할 수 있습니다. 
public class CustomerTest {
	public static void main(String[] args) {
		Customer customer = new Customer();
		
		Buy buyer = customer;
		buyer.buy();
		
		Sell seller = customer;
		seller.sell();
		
		if(seller instanceof Customer) {
			Customer customer2 = (Customer)seller;
			customer2.buy();
			customer2.sell();
		}
	}
}
  • 위 코드를 보면 Customer 형으로 선언된 customer 변수를 Buy와 Sell 형으로 대입할 수 있다.
  • 위와 같이 형 변환이 일어나면 해당하는 형에서 사용 가능한 메서드와 상수만 사용 가능합니다. 
  • 또한 상속에서와 마찬가지로 원래의 인스턴스 자료형으로 다운 캐스팅하기 위해서는 instanceof 를 사용하여 본래 인턴스 자료형으로 안전하게 변환할 수 있습니다. 

두 인터페이스의 디폴트 메서드가 중복되는 경우

두 인터페이스의 정적 메서드가 중복되는 경우에는 문제가 되지 않는다.

  • 정적 메서드는 인스턴스 생성과 상관없이 사용할 수 있습니다. 
  • Customer 클래스가 Buy, Sell 두 인터페이스를 구현하고 Buy 인터페이스와 Sell 인터페이스에 똑같은 pay( ) 라는 정적 메서드가 있었다면 어떻게 될까요? 
  • 정답은 아무런 문제가 발생하지 않는다는 겁니다. 
  • 왜냐하면 Buy.pay( ) 와 Sell.pay( ) 로 호출하는 명이 다르기 때문입니다. 

두 인터페이스의 디폴트 메서드가 중복되는 경우에는 문제가 된다.

  • 왜냐하면 디폴트 메서드는 인스턴스를 생성해야 호출할 수 있는 메서드이기 때문에 이름이 같으면 모호성이 발생하여 문제가 발생합니다. 
  • "Duplicate default methods named order with the ... 이 오류 메시지는 디폴트 메서드가 중복되었으니 두 인터페이스를 구현하는 Customer 클래스에서 재정의하라는 뜻입니다. 
  • Customer 클래스에서 디폴트 메서드를 재정의하면, Customer 클래스를 생성하여 사용할 때 재정의된 메서드가 호출됩니다. 
  • 여기서 주의할 점이 하나 있습니다.
  • 이전에 배운 자바 가상 메서드 원리가 바로 여기서도 적용된다는 점입니다. 
  • 다음 코드를 보겠습니다. 
public class CustomerTest {
	public static void main(String[] args) {
		Customer customer = new Customer();
		
		Buy buyer = customer;
		buyer.buy();
		buyer.order(); // 가상 메서드 발생
		
		Sell seller = customer;
		seller.sell();
		seller.order();  // 가상 메서드 발생
		
		if(seller instanceof Customer) {
			Customer customer2 = (Customer)seller;
			customer2.buy();
			customer2.sell();
			customer2.order();
		}
	}
}
  • 인스턴스에 order 라는 default 메서드를 만든 이후에 Test 코드입니다. 
  • 위 코드에서 Customer 가 Buy 형으로 변환되고 buyer.order( )를 호출하면 Buy 에 구현한 디폴트 메서드가 아닌 Customer 클래스에 재정의한 메서드가 호출됩니다. 
  • 이전에 배운 자바 가상 메서드 원리와 완벽히 동일합니다. 

인터페이스 상속하기 

  • 인터페이스 간에도 상속이 가능합니다. 
  • 인터페이스 간 상속은 구현 코드를 통해 기능을 상속 하는 것이 아닙니다.
  • 그렇기에 인터페이스 간 상속을 형 상속(type inheritance) 라고 합니다. 
  • 클래스의 경우에 하나의 클래스에 하나의 클래스만 상속받을 수 있었지만, 인터페이스는 여러 개를 동시에 상속받을 수 있습니다. 
  • 한 인터페이스가 여러 인터페이스를 상속 받으면 상속 받은 인터페이스는 상위 인터페이스에 선언한 추상 메서드를 모두 가지게 됩니다. 
  • 다음 코드를 보면서 배워보겠습니다.
public interface X {
	void x();
}

 

public interface Y {
	void y();
}
  • 우선 상속 받을 인터페이스 2개를 만들었습니다.
public interface MyInterface extends X, Y {
	void myMethod();
}
  • 2개 인터페이스를 상속받는 인터페이스를 만들었습니다.
  • 그렇다면 이 myMethod 인터페이스를 구현한 클래스는 몇개의 추상 메서드를 구현해야 할까요?
  • 정답은 3개입니다. 

인터페이스 구현과 클래스 상속 함께 쓰기

  • 한 클래스에서 클래스 상속과 인터페이스 구현을 모두 할 수도 있습니다. 
  • Queue 인터페이스를 구현하고 shelf 클래스를 상속받는 BookShelf 클래스를 구현해보면서 배워보겠습니다.
public class Shelf {
	protected ArrayList<String> shelf;
	
	public Shelf() {
		shelf = new ArrayList<String>();
	}

	public ArrayList<String> getShelf() {
		return shelf;
	}

	public int getCount() {
		return shelf.size();
	}	
}
  • 먼저 상속 받을 Shelf 클래스를 구현했습니다. 
public interface Queue {
	void enQueue(String title);  // 배열이 맨 마지막에 추가
	String deQueue();  // 배열의 맨 처음 항목 제거 후 해당 값 반환
	int getSize();  // 현재 Queue에 있는 개수 반환
}
  • 구현될 Queue interface 를 구현했습니다. 
public class Bookshelf extends Shelf implements Queue {

	@Override
	public void enQueue(String title) {
		shelf.add(title);
	}

	@Override
	public String deQueue() {
		return shelf.remove(0);
	}

	@Override
	public int getSize() {
		return getCount();
	}

}
  • Shelf 클래스를 상속받고 Queue 인터페이스를 구현한 BookShelf 클래스를 구현했습니다.
  • ArrayList  라는 클래스를 사용한 것을 볼 수 있는데, 앞으로 자바 프로그램을 개발하면서 이미 제공되고 있는 클래스나 인터페이스를 사용한 프로그램을 자주 접하게 됩니다. 
  • 특히 실무에서는 프레임워크나 기존 소스 코드를 사용해 개발하는 경우가 많습니다. 

실무에서 인터페이스를 사용하는 경우

  • 인터페이스는 기본적인 용도가 해당 인터페이스를 구현한 클래스가 제공할 기능을 선언하고 설계하는 것입니다.
  • 만약 여러 클래스가 같은 메서드를 서로 다르게 구현한다면 어떻게 해야 할까요? 
  • 인터페이스에 메서드를 선언(추상 메서드로 자동 변환 public abstract) 한 다음에 인터페이스를 구현한 각 클래스에서 같은 메서드에 대해 다양한 기능을 구현하면 됩니다. 
  • 이것이 바로 인터페이스를 이용한 다형성의 구현입니다. 
  • 이런 경우를 생각해 봅시다. 
  • 어느 회사에서 시스템을 개발하였는데, 이 시스템은 자료를 저장하기 위해 데이터베이스를 사용합니다.
  • 그런데, 고객 사들이 사용하는 데이터 베이스가 모두 다른 겁니다. 
  • 이런 경우 데이터 베이스와 연관되는 코드만 따로 모아서 작업을 해주어야 합니다. 
  • 그 후 데이터베이스 기능을 수행할 인터페이스를 정의한 다음 인터페이스 저으이에 맞게 여러 데이터베이스 관련 모듈을 개발하면 됩니다. 
  • 이렇게 인터페이스를 잘 정의하는 것이 확장성 있는 프로그램을 만드는 시작입니다.

JDBC와 인터페이스

  • JDBC 는 Java DataBase Connectivity 의 약자입니다. 
  • 자바와 데이터베이스를 연결해 주는 역할을 합니다. 
  • 자바와 데이터베이스를 연결하려면 여러 기능을 수행해야 하는데 그 중 하나가 Connection을 생성하고 연결하는 것입니다. 
  • JavaDoc 에서 Connection을 찾아보면 다음과 같은 내용을 볼 수 있습니다.
  • 자바와 데이터베이스를 연결하기 위해 사용하는 Connection은 인터페이스입니다. 
  • 이 인터페이스에는 여러 메서드들이 미리 구현되어 있으며 이들 메서드를 사용하여 데이터베이스에 접근하는 자바 프로그램을 구현하면 됩니다. 
  • 그렇다면 Connection 과 같은 JDBC 인터페이스는 누가 구현한 걸까요? 
  • 이 인터페이스는 오라클, MySQL 등 데이터베이스 프로그램을 만드는 회사에서 구현합니다. 
  • 데이터베이스 회사가 자신의 회사 데이터베이스에 맞게 구현한 클래스 파일 묶음인 .jar 라이브러리를 제공하기 때문에, 우리는 프로그래밍을 하면서 제공된 라이브러리를 로딩하여 Connection 인터페이스에 선언된 메서드를 사용하기만 하면 됩니다. 
  • 정리해보면 JDBC 는 자바에서 데이터베이스를 어떻게 사용할 것인지를 기술한 명세, 즉 인터페이스이자 약속인 셈입니다.