본 포스트는 카카오 테크 캠퍼스 1기에서 제공하는 패스트캠퍼스 강의에서 배운 내용을 토대로 정리하였습니다.

카카오 테크 캠퍼스

인터페이스란?


인터페이스(Interface)는 클래스 또는 객체의 기능을 외부에 노출시키는 방법 중 하나이다.

즉, 인터페이스는 객체 간의 상호작용을 돕는 규약이나 계약, 틀이라고 볼 수 있다.

인터페이스는 일종의 추상화된 클래스로, 클래스와 비슷한 형태를 갖지만 실제로는 구현된 코드가 없다.

대신 인터페이스는 메소드, 상수 등의 특정 요소를 선언하여, 해당 인터페이스를 구현한 클래스가 반드시 가져야 하는 특정한 기능이나 속성을 정의한다.

이를 통해 객체 간의 상호작용에서 규격화된 방식으로 데이터를 주고받을 수 있다.

ex)
ClickAble 이라는 인터페이스를 상속한 클래스는 외부에서 상호작용 할떄,

이 클래스는 클릭이 가능한 기능을 가지고있고, 그 기능(메서드)의 인풋(매개변수) 과 아웃풋(결과) 를 통해 대략적으로 어떠한 기능인지 알 수 있다.

요약

  • 클래스나 프로그램이 제공하는 기능을 명시적으로 선언

  • 일종의 클라이언트 코드와의 약속이며 클래스나 프로그램이 제공하는 명세(specification)

  • 클라이언트 프로그램은 인터페이스에 선언된 메서드 명세만 보고 이를 구현한 클래스를 사용할 수 있음

  • 어떤 객체가 하나의 인터페이스 타입이라는 것은 그 인터페이스가 제공하는 모든 메서드를 구현했다는 의미임

  • 인터페이스를 구현한 다양한 객체를 사용함 - 다형성

구현

  • 모든 메서드가 추상 메서드로 선언됨 public abstract

  • 모든 변수는 상수로 선언됨 public static final

  • 인터페이스를 클래스가 상속할 경우, 그 인터페이스의 모든 추상메소드를 구현해야함.

interface 인터페이스 이름{

    public static final float pi = 3.14F;
    public void makeSomething();
}
  • 자바 8 부터 디폴트 메서드(default method)와 정적 메서드(static method) 기능의 제공으로 일부 구현 코드가 있음

예시

  1. 사칙연산 기능이 있음을 알리는 calc 인터페이스 구현

  2. 1차 기능인 더하기 ,빼기 기능만 구현된 Calculator 추상 클래스 구현
    => 인터페이스를 상속했는데 전부 구현하지 않아서 추상 클래스임

  3. calc의 모든 기능이 구현된 CompleteCalc 클래스 구현
    => CompleteCalc 클래스는 추상클래스가 아니라서 인스턴스 생성가능



Calc.java

public interface Calc {

	double PI = 3.14;
	int ERROR = -99999999;
	//사칙연산의 기능이 있음을 알리는 추상 메서드 선언
    //외부에 다른 클래스와 상호작용할때 이 인터페이스를 상속한
    //클래스가 어떤 기능이 있는지 1차적으로 이해가 가능
	int add(int num1, int num2);
	int substract(int num1, int num2);
	int times(int num1, int num2);
	int divide(int num1, int num2);
	
}

Calculator.java

public abstract class Calculator implements Calc{

	@Override
	public int add(int num1, int num2) {
    //인터페이스의 메서드 구현
		return num1 + num2;
	}

	@Override
	public int substract(int num1, int num2) {
     //인터페이스의 메서드 구현
		return num1 - num2;
	}
}

CompleteCalc.java

public class CompleteCalc extends Calculator{
	
	@Override
	public int times(int num1, int num2) {
    //인터페이스의 메서드 구현
		return num1 * num2;
	}

	@Override
	public int divide(int num1, int num2) {
    //인터페이스의 메서드 구현
		if( num2 == 0 )
			return ERROR;
		else 
			return num1 / num2;
	}
	
	public void showInfo() {
		System.out.println("모두 구현하였습니다.");
	}
}

CalculatorTest.java

public class CalculatorTest {

	public static void main(String[] args) {
		Calc calc = new CompleteCalc();
		int num1 = 10;
		int num2 = 2;
		
		System.out.println(num1 + "+" + num2 + "=" + calc.add(num1, num2));
		System.out.println(num1 + "-" + num2 + "=" +calc.substract(num1, num2));
		System.out.println(num1 + "*" + num2 + "=" +calc.times(num1, num2));
		System.out.println(num1 + "/" + num2 + "=" +calc.divide(num1, num2));
	}
}

결과

인터페이스 구현과 형 변환


  • 인터페이스를 구현한 클래스는 인터페이스 형으로 선언한 변수로 형 변환 할 수 있음

위의 계산기 예시에서
Calc calc = new CompleteCalc();

  • 상속에서의 형 변환과 동일한 의미
    => 가상메서드원리(인스턴스의 가상 메서드 테이블 따름)그대로 적용!
  • 클래스 상속과 달리 구현 코드가 없으므로 여러 인터페이스를 구현할 수 있음 ( cf. extends)
  • 형 변환되는 경우 인터페이스에 선언된 메서드만을 사용가능함

인터페이스와 다형성

하나의 인터페이스를 여러 객체가 구현하게 되면 클라이언트 프로그램은 인터페이스의 메서드를 활용하여 여러 객체의 구현을 사용할 수 있음 (다형성)

예제

인터페이스를 이용한 DAO(data access object)를 구현 해보자.

  • DB에 회원 정보를 넣는 dao(data access object)를 여러 DB 제품이 지원될 수 있게 구현함

  • 환경파일(db.properties) 에서 database의 종류에 대한 정보를 읽고 그 정보에 맞게 dao 인스턴스를 생성하여 실행될 수 있게 함

  • source hierachy

  1. 우선 유저 정보 Model 클래스인 UserInfo클래스를 만들고,

  2. UserInfoDao인터페이스를 구현해서 db종류에 맞게 구체화시킨 오라클Dao, MySqlDao 파일을 각각 구현한다.

  3. 마지막으로 서버(이번 예시에는 단순 UserInfoDao 파일)와 상호작용하는 클라이언트인 UserInfoClient 파일로 테스트를 해본다.

구현

1.유저 정보 Model 클래스인 UserInfo클래스를 만들자.

UserInfo.java (사용자정보 모델 클래스)

public class UserInfo {
	
	private String userId;
	private String passwd;
	private String userName;
	
	public String getUserId() {
		return userId;
	}
	
	public void setUserId(String userId) {
		this.userId = userId;
	}
	
	public String getPasswd() {
		return passwd;
	}
	
	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}
	
	public String getUserName() {
		return userName;
	}
	
	public void setUserName(String userName) {
		this.userName = userName;
	}
}
  1. UserInfoDao인터페이스를 구현해서 db종류에 맞게 구체화시킨 오라클Dao, MySqlDao 파일을 각각 구현하자.

UserInfoDao.java
( dao 에서 제공되어야 할 메서드를 선언한 기본 인터페이스 )

public interface UserInfoDao {

	void insertUserInfo(UserInfo userInfo);
	void updateUserInfo(UserInfo userInfo);
	void deleteUserInf(UserInfo userInfo);
}

UserInfoMySqlDao.java (UserInfoDao 인터페이스를 구현한 MySql 버전 dao)

public class UserInfoMySqlDao implements UserInfoDao{

	@Override
	public void insertUserInfo(UserInfo userInfo) {
		System.out.println("insert into MYSQL DB userId =" + userInfo.getUserId() );		
	}

	@Override
	public void updateUserInfo(UserInfo userInfo) {
		System.out.println("update into MYSQL DB userId = " + userInfo.getUserId());		
	}

	@Override
	public void deleteUserInf(UserInfo userInfo) {
		System.out.println("delete from MYSQL DB userId = " + userInfo.getUserId());
		
	}

}

UserInfoOracleDao.java (UserInfoDao 인터페이스를 구현한 Oracle 버전 dao)

public class UserInfoOracleDao implements UserInfoDao{

	public void insertUserInfo(UserInfo userInfo){
		System.out.println("insert into ORACLE DB userId =" + userInfo.getUserId() );
	}
	
	public void updateUserInfo(UserInfo userInfo){
		System.out.println("update into ORACLE DB userId = " + userInfo.getUserId());
	}
	
	public void deleteUserInf(UserInfo userInfo){
		System.out.println("delete from ORACLE DB userId = " + userInfo.getUserId());
	}
}
  1. 마지막으로 UserInfoDao와 상호작용하는 클라이언트인 UserInfoClient 클래스를 만들자.

UserInfoClient.java (UserInfoDao 인터페이스를 활용하는 클라이언트 프로그램)

public class UserInfoClient {

	public static void main(String[] args) throws IOException {

		FileInputStream fis = new FileInputStream("db.properties");
		//파일입출력 스트림에 db.properties파일을 추가
		Properties prop = new Properties();
        //프로퍼티 자료형을 하나 선언해서
		prop.load(fis);
		// 위에 선언한 fis 파일을 로드한다.
		String dbType = prop.getProperty("DBTYPE");
		// 그 fis파일, 즉 db.properties 파일의 
        // DBTYPE 을 dbType변수에 저장
		UserInfo userInfo = new UserInfo();
		userInfo.setUserId("12345");
		userInfo.setPasswd("!@#$%");
		userInfo.setUserName("이순신");
		//위에 처럼 더미 유저정보를 넣어주고
		
		UserInfoDao userInfoDao = null;
		
        //아래와 같이 dbType에 따라 다른 Dao를 실행시킨다.
        // => 다형성 을 보여줌.
		if(dbType.equals("ORACLE")){
			userInfoDao = new UserInfoOracleDao();
		}
		else if(dbType.endsWith("MYSQL")){
			userInfoDao = new UserInfoMySqlDao();
		}
		else{
			System.out.println("db support error");
			return;
		}
		
		userInfoDao.insertUserInfo(userInfo);
		userInfoDao.updateUserInfo(userInfo);
		userInfoDao.deleteUserInf(userInfo);
	}
}

결과

db.properties 환경파일이 MYSQL 일때

"DBTYPE=MYSQL"

db.properties 환경파일이 ORACLE 일때

"DBTYPE=ORACLE"

인터페이스의 여러가지 메서드


디폴트 메서드 (자바 8이후)

  • 구현을 가지는 메서드, 인터페이스를 구현하는 클래스들에서 공통으로 사용할 수 있는 기본 메서드

  • 인터페이스를 상속한 인터페이스가 부모 인터페이스를 오버라이딩하려면 디폴트 메서드를 이용하면 된다.

    => 밑에 인터페이스의 인터페이스 다중 상속 항목에서 예제의 MyInterface.java 참조

  • default 키워드 사용

default void description() {
	System.out.println("정수 계산기를 구현합니다.");
	myMethod();
}
  • 구현 하는 클래스에서 재정의 할 수 있음
@Override
public void description() {
	System.out.println("CompleteCalc에서 재정의한 default 메서드");
	//super.description();
}
  • 인터페이스를 구현한 클래스의 인스턴스가 생성 되어야 사용 가능함

디폴트메서드의 다이아몬드 상속 문제

  • 부모 인터페이스 A,B를 자식 인터페이스 C가 상속한다고 할때
    =>
    A,B 모두 이름이 같은 디폴트메서드가 중복될경우 C에서 반드시 오버라이드 해서,
    메서드 호출시 모호함을 해결해야함
  1. A.super.디폴트메서드명 으로 A의 메서드를 불러오거나
  2. B.super.디폴트메서드명 으로 B의 메서드를 불러오거나
  3. 그냥 새로 재정의하면 된다.

A.java

public interface A {
	void Test(){
    System.out.println("A의 디폴트 메서드");
    }
}

B.java

public interface B {
	void Test(){
    System.out.println("B의 디폴트 메서드");
    }
}

C.java

public interface C extends A, B{
	//1. A의 메서드 불러옴
	@Override
	void Test(){
    A.super.Test();
    }
    
    //2. B의 메서드 불러옴
    @Override
	void Test(){
    B.super.Test();
    }

	//3. 새로 재정의 함.
	@Override
	void Test(){
    System.out.println("C의 디폴트 메서드");
    }
    
    //4. 당연히, 1,2,3 조합도 가능. 어차피 재정의 하는거니까.
    @Override
	void Test(){
    A.super.Test();
    B.super.Test();
    System.out.println("C의 디폴트 메서드");
    }
}

정적 메서드 (자바 8이후)

  • 인스턴스 생성과 상관 없이 인터페이스 타입으로 사용할 수 있는 메서드

  • 클래스명으로 접근

  • 인스턴스 안만들고 접근 가능

static int total(int[] arr) {
	int total = 0;
		
	for(int i: arr) {
		total += i;
	}
	mystaticMethod();
	return total;
}

private 메서드 (자바 9이후)

  • 인터페이스를 구현한 클래스에서 사용하거나 재정의 할 수 없음

  • 인터페이스 내부에서만 사용하기 위해 구현하는 메서드

  • default 메서드나 static 메서드에서 사용함
    => 당연하지. 왜냐면 이 두 유형의 메서드만 구현부가 있으니까.

private void myMethod() {
	System.out.println("private method");
}
	
private static void mystaticMethod() {
	System.out.println("private static method");
}

인터페이스 상속


클래스의 인터페이스 다중 구현

  • 자바에서 클래스와 달리 인터페이스는 한 클래스가 다중 구현 할 수 있음

  • 디폴트 메서드가 중복 되는 경우는 구현 하는 클래스에서 재정의 하여야 함

  • 여러 인터페이스를 구현한 클래스는 인터페이스 타입으로 형 변환 되는 경우 해당 인터페이스에 선언된 메서드만 사용 가능 함

예제

Sell.java

public interface Sell {
	void sell();
}

Buy.java

public interface Buy {
	void buy();
}

Customer.java

public class Customer implements Buy, Sell{

	@Override
	public void sell() {
		System.out.println("customer sell");
	}

	@Override
	public void buy() {
		System.out.println("customer buy");		
	}

	public void sayHello() {
		System.out.println("Hello");
	}
}

CustomerTest.java

public class CustomerTest {

	public static void main(String[] args) {

		Customer customer = new Customer();
		customer.buy();
		customer.sell();
		customer.sayHello();
		//Customer 클래스의 모든 메서드 실행가능
        
        
		Buy buyer = customer;
		buyer.buy();
        //Buy 클래스에 buy메서드만 선언돼 있어서 buy메서드만 실행가능
        //가상메서드 원리에 의해 Customer의 buy메서드 로직 실행
		
		Sell seller = customer;
		seller.sell();
         //Buy 클래스에 sell메서드만 선언돼 있어서 sell메서드만 실행가능
        //가상메서드 원리에 의해 Customer의 sell메서드 로직 실행

	}
}

인터페이스의 인터페이스 다중 상속

  • 인터페이스 사이에도 상속을 사용할 수 있음

  • extends 키워드를 사용

  • 인터페이스는 다중 상속이 가능하고 구현 코드의 상속이 아니므로 타입 상속 이라고 함

예제

X.java

public interface X {
	void x();
}

Y.java

public interface Y {
	void y();
}

MyInterface.java

public interface MyInterface extends X, Y{
	void myMethod();
     
   	@Override
   	default void x(){
   	//default 키워드로 통해 인터페이스X의 메서드 오버라이딩
    //유의미한 행위는 아님. 어차피 Myinterface 를 구현하는 클래스에서 x메서드를 구현할텐데
    //키워드 의미 그대로 해당 인터페이스를 구현할떄 기본값으로 두기위해 사용할 수도? 
   	System.out.println("this is MyInterFace X);
   };
}

MyClass.java

public class MyClass implements MyInterface{



	@Override
	public void y() {
		System.out.println("y()");		
	}

	@Override
	public void myMethod() {
		System.out.println("myMethod()");		
	}
}

MyClassTest.java

public class MyClassTest {

	public static void main(String[] args) {

		MyClass mClass = new MyClass();
		
		X xClass = mClass;
		xClass.x();
        //this is MyInterFace X 출력됨
		
		
		Y yClass = mClass;
		yClass.y();
		//y() 출력됨
        
		MyClass iClass = mClass;
		iClass.x();
         //this is MyInterFace X 출력됨
		iClass.y();
        //y() 출력됨
		iClass.myMethod();
        //myMethod()
	}

}
profile
새로운 여정은 언제나 두렵고 동시에 흥미로 가득 차 있다.

0개의 댓글