[Java] 인터페이스는 여러 클래스에서 사용할 멤버를 일관되게 하기 위한 기술 명세이다.

김선호·2022년 8월 2일
1

Java 문법

목록 보기
9/15
post-thumbnail

💡 인터페이스는 여러 클래스에서 사용할 멤버(필드, 메서드) 를 일관되게 하기 위한 기술 명세이다.

인터페이스


인터페이스란?

아이폰과 갤럭시를 위한 프로그램을 개발한다고 생각해보자. 두 스마트폰은 구현 방식과 실행 플랫폼이 다르므로 각각 전용 프로그램을 개발해야하고, 사용자 또한 아이폰과 갤럭시 두 스마트폰 모두 프로그램 사용법을 익혀야 할 것이다. 그럼 구현 방식과 실행 플랫폼은 다르더라도, 사용법만은 일관되게 하려면 어떻게 해야할까? 그것은 바로 다음과 같은 기준을 정함으로써 가능하다.

  • 기능별로 클래스를 정한다.
  • 기능별로 메서드의 선언부를 정한다.
  • 기능별로 변수를 정한다.

즉 이렇게 기준을 정함으로써(=인터페이스를 통일함으로써) 사용자는 플랫폼에 상관없이 일관된 방법으로 프로그램을 사용할 수 있다.
자바에서는 인터페이스(interface) 가 위의 역할을 해주며, 이로 인해 프로그램을 개발하기 전에 기능별로 클래스, 변수, 메서드를 정할 수 있다. 인터페이스는 여러 클래스에서 사용할 멤버(필드, 메서드) 를 일관되게 하기 위한 기술 명세인 것이다.


인터페이스 선언 문법

자바에서 인터페이스는 다음과 같이 선언한다. 인터페이스에서 멤버를 선언할 때 접근 제한자는 반드시 public 으로 선언할 것에 유념한다.

제어자 interface 인터페이스명 {
	// 모든 멤버의 접근 제한자는 public으로 선언 
	public static fianl 변수 선언;
	public abstract 메서드 선언(); // 구현부 없음(추상 메서드)
	public default 메서드 선언() {} 
	public static 메서드 선언() {} 
}

선언 문법에서 확인할 수 있듯이, 인터페이스는 자체적으로 속성과 기능을 가진 객체는 아니고, 다른 클래스에서 구현할 내용에 대해 형식만 갖춘 것이다.


[1] 필드 선언

public interface Messenger {
	int MIN_SIZE = 1; // 컴파일 시 'public static final' 제어자 자동으로 추가
	int MAX_SIZE = 104857600; // 컴파일 시 'public static final' 제어자 자동으로 추가
} 
  • 인터페이스로 객체를 생성할 수는 없다. 따라서 인터페이스에 선언되는 필드는 객체 생성과 상관없이 사용할 수 있는 static으로 선언해야한다.
  • 필드 선언 시 별도로 public static final 을 지정하지 않으면 컴파일러가 컴파일 시 자동으로 이를 추가한다.

[2] 메서드 선언

// 인터페이스 본문에 메서드 선언 
public String getMessage();
public String setMessage(String msg); 

// 메서드는 컴파일 시 abstract 제어자가 자동으로 추가된다.
public abstract String getMessage();
public abstract String setMesage(String msg);
  • 인터페이스에 선언하는 메서드는 실제 내용을 구현할 목적이 아닌 인터페이스 통일을 목적으로 선언하는 메서드이므로, 본문을 구현하지 않는다(추상 메서드로 선언한다).
  • 인터페이스에 선언된 메서드는 (생략하면) 컴파일 시 public abstract 제어자가 자동으로 추가된다.

[3] default 메서드 선언

default 메서드와 static 메서드

자바 7까지는 인터페이스에 public static final 필드와 public abstract 메서드 선언만 가능했으나, 자바 8 부터 default 메서드, static 메서드 가 추가되어 본문을 갖는 메서드를 인터페이스에 선언할 수 있게 되었다.

default 메서드 선언 문법

public default void setLogin(boolean login) {
	if (login) {
		System.out.println("로그인 처리합니다");
	else {
		System.out.println("로그아웃 처리합니다.");
	}
}
  • default 메서드는 인터페이스를 사용하는 모든 클래스에서 공통적으로 갖는 기능을 구현할 목적으로 사용한다.
  • public 접근 제한자는 생략하면 컴파일 시 자동으로 추가된다.

[4] static 메서드 선언

public static void getConnection() {
	System.out.println("network에 연결합니다");
}
  • 인터페이스의 static 메서드는 해당 인터페이스를 구현하고 있는 모든 객체에서 자주 사용하는 유용한 기능을 제공하기 위해서 사용된다 .
  • 인터페이스의 static메서드는 기존의 클래스 메서드(정적 메서드)와 같다. 프로그램 시작 시 코드 메모리 영역에 사용 준비가 완료되므로 인스턴스 생성과 무관하게 사용 가능하다.
  • public 접근 제한자는 생략하면 컴파일 시 자동으로 추가된다.
  • “인터페이스명.메서드명()” 으로 호출해서 사용한다.

[5] private 메서드 선언

private void log() { // 같은 인터페이스의 default 메서드에서 사용하기 위해 선언된 private 메서드
	System.out.println("start job!");
}

default void setLogin() { // default 메서드에서 private 메서드를 사용한다(같은 인터페이스 내). 
	log();
}
  • private 메서드동일한 인터페이스에 선언된 default메서드에서 사용할 목적으로 본문을 구현하는 메서드이다.

인터페이스 예제 코드

/* 인터페이스 예제 코드 Messenger.java */
public interface Messenger {

    public static final int MIN_SIZE = 1; // 'public static final' 생략시 자동으로 추가
    public static final int MAX_SIZE = 104857600;

    public abstract String getMessage(); // 'abstract' 생략 시 자동으로 추가

    public abstract void setMessage(String msg);

    // default 메서드 : 인터페이스를 사용하는 모든 클래스에서 공통으로 갖는 기능을 구현
    default void setLogin(boolean login) {
        log();
        if (login) {
            System.out.println("로그인 처리합니다.");
        } else {
            System.out.println("로그아웃 처리합니다.");
        }
    }

    // static 메서드 : 인터페이스를 구현하는 모든 객체에서 자주 사용하는 유용한 기능을 제공
    public static void getConnection() {
        System.out.println("network에 연결합니다.");
    }

    // private 메서드 : 동일한 인터페이스에 선언된 default 메서드에서만 사용할 목적으로 본문을 구현하는 메서드
    private void log() {
        System.out.println("start job!");
    }
}

인터페이스 상속

[1] 단일 상속

  • 클래스 설계 시 인터페이스에서 기술한 내용을 그대로 구현하는 것을 “인터페이스를 구현한다.” 또는 “상속한다.” 라고 표현한다.
  • 클래스를 상속할 때는 extends 클래스명 으로 선언하고, 인터페이스를 상속할 때는 implements 클래스명 으로 선언한다.

인터페이스 상속 문법

제어자 class 클래스명 extends 부모 클래스명 implements 인터페이스명 {
}

인터페이스 상속 예제

/* Messenger 인터페이스 */
public interface Messenger {

    public static final int MIN_SIZE = 1; 
    public static final int MAX_SIZE = 104857600;

    public abstract String getMessage(); 
    public abstract void setMessage(String msg);

    default void setLogin(boolean login) {
        log();
        if (login) {
            System.out.println("로그인 처리합니다.");
        } else {
            System.out.println("로그아웃 처리합니다.");
        }
    }

    public static void getConnection() {
        System.out.println("network에 연결합니다.");
    }

    private void log() {
        System.out.println("start job!");
    }
}

/* Messenger 인터페이스를 상속하는 IPhoneManager, GalaxyManager 클래스 */
public class IPhoneManager implements Messenger {
}

public class GalaxyManager implements Messenger {
}

위처럼 Messenger 인터페이스를 상속하면 곧바로 컴파일 오류가 발생하는데, 이것은 인터페이스를 상속한 클래스에서 인터페이스의 추상메서드를 오버라이딩 해주지 않았기 때문이다. 추상메서드를 포함하는 인터페이스를 상속하는 클래스는 반드시 추상 메서드를 오버라이딩 해주어야한다.


추상메서드를 오버라이딩한 클래스 예제

public class IPhoneMessenger implements Messenger {

    public String getMessage() { // 오버라이딩
        return "iphone";
    }

    public void setMessage(String msg) { // 오버라이딩
        System.out.println("iPhone에서 메시지를 설정합니다 : " + msg);
    }

    public void clearMessage() {
        System.out.println("좌우로 흔들어 문자열을 지웁니다.");
    }
} 

public class GalaxyMessenger implements Messenger{
    public String getMessage() { // 오버라이딩
        return "galaxy";
    }

    public void setMessage(String msg) { // 오버라이딩
        System.out.println("galaxy에서 메시지를 설정합니다 : " + msg);
    }

    public void changeKeyboard() {
        System.out.println("키보드아이콘 터치 후 키보드를 변경합니다.");
    }
}
// 인스턴스 생성 후 사용 예제 
public class MessengerTest {
    public static void main(String[] args) {

        IPhoneMessenger iphone = new IPhoneMessenger();
        GalaxyMessenger galaxy = new GalaxyMessenger();

        System.out.println("메신저 최소 문자크기 : " + Messenger.MIN_SIZE);
        System.out.println("메신저 최대 문자크기 : " + Messenger.MAX_SIZE);

        iphone.setLogin(true);
        iphone.getMessage();
        iphone.setMessage("hello");
        iphone.clearMessage();

        galaxy.setLogin(true);
        galaxy.getMessage();
        galaxy.setMessage("hi");
        galaxy.changeKeyboard();
    }
}

이처럼, 인터페이스를 만들고 비슷한 기능을 처리하는 곳에서 인터페이스를 상속하여 구현하면 일관된 구조를 가질 수 있고 유지 보수하기가 수월해진다.


[2] 다중 상속

  • 자바는 클래스를 상속할 때 단일 상속만 가능하지만, 인터페이스는 여러 개를 상속할 수 있다.
  • 여러 개의 인터페이스를 상속하는 것을 다중 상속이라고 한다.

인터페이스 다중 상속 문법

제어자 class 클래스명 extends 부모클래스명 implements 인터페이스명, 인터페이스명, ... {
}

인터페이스 다중 상속 예제

// WorkFile.java
public interface WorkFile {

    public void fileUpload(); // 컴파일 시 abstract 자동으로 추가
    public void fileDownload(); // 컴파일 시 abstract 자동으로 추가
}

// Messeger, WorkFile 인터페이스를 상속하는(다중 상속) GalaxyMessenger 클래스
public class GalaxyMessenger implements Messenger, WorkFile{
    public String getMessage() { // Messenger 인터페이스 메서드 오버라이딩
        return "galaxy";
    }

    public void setMessage(String msg) { // Messenger 인터페이스 메서드 오버라이딩
        System.out.println("galaxy에서 메시지를 설정합니다 : " + msg);
    }

    public void fileUpload() { // WorkFile 인터페이스 오버라이딩
        System.out.println("file을 업로드합니다.");
    }

    public void fileDownload() { // WorkFile 인터페이스 오버라이딩
        System.out.println("file을 다운로드합니다.");
    }

    public void changeKeyboard() {
        System.out.println("키보드아이콘 터치 후 키보드를 변경합니다.");
    }
}

[3] 복합 상속

  • 클래스는 하나의 클래스와 여러 개의 인터페이스를 동시에 상속받는 복합 상속이 가능하다.

복합 상속 예제

// GraphicIOS.java
public class GraphicIOS {

    public void draw_textBox() {
        System.out.println("텍스트 상자를 그린다.");
    }

    public void draw_submitButton() {
        System.out.println("전송 버튼을 그린다.");
    }
}

// GraphicIOS 클래스와 Messenger 인터페이스를 상속하는 (복합 상속) IphoneMessenger 클래스 
public class IPhoneMessenger extends GraphicIOS implements Messenger {

    public String getMessage() { // 오버라이딩
        return "iphone";
    }

    public void setMessage(String msg) { // 오버라이딩
        System.out.println("iPhone에서 메시지를 설정합니다 : " + msg);
    }

    public void clearMessage() {
        System.out.println("좌우로 흔들어 문자열을 지웁니다.");
    }
}

✍ 본 포스팅은 책 ‘처음 해보는 자바 프로그래밍(오정임 저, 루비페이퍼)’을 읽고 정리한 글입니다.

profile
Every Run, Learn Counts.

0개의 댓글