[백기선님과 함께하는 Live-Study] 8주차 - 인터페이스

JoonYoung Maeng·2021년 1월 5일
0
post-thumbnail

✔️ 목표

자바의 인터페이스에 대해 학습하세요.

✔️ 학습할 것 (필수)

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

💡 인터페이스 정의하는 방법

인터페이스(Interface)란 메소드에 대한 선언만 한 상태로 모든 메소드를 추상화로 정의한 상태를 말한다.

인터페이스를 정의하기 위해서는 interface 키워드를 이용하여 선언한다.

인터페이스는 반드시 상수추상 메소드만 사용이 가능하다.

package com.livestudy.eighth;

public interface Phone {
		//컴파일러가 public static final을 붙혀 컴파일함
		//반드시 상수 선언
    int YEAR = 2021;

		//추상 메소드
    void setSerialNumber(int serialNumber);
}

✏️ 인터페이스 장점

  1. 동시적인 개발을 통한 개발 시간 단축
  2. 틀을 인터페이스로 만들고 해당 인터페이스를 구현하여 확장이 용이
  3. 관계가 없는 클래스 간의 공통적인 부분이 있는 경우, 인터페이스를 통해 공통 부분 관계 형성 가능
  4. 인터페이스를 이용하여 선언과 구현을 분리함으로써 클래스 간의 변경(결합도)에 영향을 미치지 않도록한다.

💡 인터페이스 구현하는 방법

인터페이스를 구현하기 위해서는 implements 키워드를 이용하여 구현한다.

인터페이스에 선언된 추상 메소드는 반드시 오버라이딩해서 사용해야하며, 인터페이스에 선언되어 있는 변수는 상수이므로 값을 변경할 수 없음에 유의한다.

package com.livestudy.eighth;

public class Galaxy implements Phone{
    private int serialNumber;

    @Override
    public void setSerialNumber(int serialNumber) {
        this.serialNumber = serialNumber;
    }

    public void printYear() {
        System.out.println(YEAR);     //2021 출력
//        YEAR = 10;
    }

    public int getSerialNumber() {
        return serialNumber;
    }
}

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

Phone 인터페이스 타입으로 Galaxy 구현체를 사용한다면 Phone 인터페이스에 선언된 setSerialNumber() 메소드만 사용가능함을 확인할 수 있다.

package com.livestudy.eighth;

public class PhoneApp {
    public static void main(String[] args) {
        //래퍼런스로 사용하는 방법
        Phone phone = new Galaxy();
        phone.setSerialNumber(100);
    }
}

image

하지만 Phone 인터페이스를 구현한 Galaxy 타입으로 Galaxy 구현체를 사용한다면 오버라이딩한 setSerialNumber() 메소드 뿐만 아니라 getSerialNumber() 메소드까지 사용이 가능하다.

package com.livestudy.eighth;

public class PhoneApp {
    public static void main(String[] args) {
        //래퍼런스로 사용하는 방법
        Phone phone = new Galaxy();
        phone.setSerialNumber(100);

        //인스턴스로 사용하는 방법
        Galaxy galaxy = new Galaxy();
        galaxy.setSerialNumber(200);

        int serialNumber = galaxy.getSerialNumber();
        System.out.println(serialNumber);
    }
}

인터페이스 레퍼러스 타입으로 선언된 구현체에서 인터페이스에 선언된 메소드만 사용됨을 확인했다.

따라서, 인터페이스 레퍼런스 타입으로 선언된 구현체에서도 구현체에서 확장한 메소드를 사용하기 위해선 구현체 타입으로 캐스팅이 필요하다.

Phone 인터페이스로 선언되고 Galaxy 구현체를 통해 이를 사용하는 경우 (Galaxy)로 캐스팅해 구현체의 메소드에 접근이 가능하다.

package com.livestudy.eighth;

public class PhoneApp {
    public static void main(String[] args) {
        //래퍼런스로 사용하는 방법
        Phone phone = new Galaxy();
        phone.setSerialNumber(100);

        //인스턴스로 사용하는 방법
        Galaxy galaxy = new Galaxy();
        galaxy.setSerialNumber(200);

        int serialNumber = galaxy.getSerialNumber();
        System.out.println(serialNumber);
        
				//캐스팅을 통한 구현체 메소드 접근 가능
        int serialNumber1 = ((Galaxy) phone).getSerialNumber();
        System.out.println(serialNumber1);
    }
}

💡 인터페이스 상속

클래스가 단일 상속만 지원하는 반면에 인터페이스는 다중 상속을 지원한다.

Phone 인터페이스와 Camera 인터페이스가 각각 있다고 하자.

package com.livestudy.eighth;

public interface Phone {
    int YEAR = 2021;
    void setSerialNumber(int serialNumber);
}

////

package com.livestudy.eighth;

public interface Camera {
    void shootPhoto(int time);
    void shootVideo(int time);
}

IPhone이라는 구현체가 Phone, Camera 인터페이스를 다중 상속 한다면 IPhone 구현체는 두 개의 인터페이스에 선언된 메소드를 모두 오버라이딩해서 사용해야한다.

package com.livestudy.eighth;

public class IPhone implements Phone, Camera{
    private int serialNumber;
    //Camera Method
    @Override
    public void shootPhoto(int time) {
        System.out.println("Shooting Photo for "+time+" second" );
    }

    @Override
    public void shootVideo(int time) {
        System.out.println("Shooting Video for "+time+" second" );
    }

    //Phone Method
    @Override
    public void setSerialNumber(int serialNumber) {
        this.serialNumber = serialNumber;
    }

    public int getSerialNumber() {
        return serialNumber;
    }
}

🤔 만약 동일한 이름을 가진 메소드가 인터페이스에 존재한다면?

만약, Camera 인터페이스에 Phone 인터페이스에 존재하는 setSerialNumber() 라는 메소드를 선언한다면, 동일한 이름의 중복된다는 컴파일 에러 메세지를 출력한다.

image

따라서, 중복된 이름의 메소드가 선언된 경우에는 다중 상속이 불가능하므로 메소드의 이름을 변경해야한다.


💡 인터페이스 기본 메소드 (Default Method), 자바 8

인터페이스는 기본적으로 메소드의 선언 지원하였다. 하지만 Java 8 부터는 인터페이스 내부에 default 접근제어자를 이용해 메소드를 정의해놓을 수 있는 default Method를 지원하기 시작하였다.

package com.livestudy.eighth;

public interface Camera {
    void shootPhoto(int time);
    void shootVideo(int time);
		
		// Default Method
    default void shoot(){
        System.out.println("Camera is shooting");
    }
}

Default Method의 등장 배경은 "하위 호환성"을 지원하기 위해서이다.

만약 Camera 인터페이스를 여러 클래스에서 구현하여 사용하고 있는 도중에 인터페이스에 대한 기능 확장이 필요하거나 보완할 부분이 생기는 경우에 일반적으로 메소드를 선언만 해놓으면 하위의 구현체에 모두 에러가 발생하고 오버라이딩을 하라는 에러 메세지가 출력될 것이다.

따라서, 이러한 충돌과 혼란을 막기위해 default method를 이용해 코드를 정의해 문제를 해결하였다. default method는 이미 구현되어 있기 때문에 하위 구현체에서 오버라이딩을 통해 재정의를 해도 되고 인터페이스에 정의된 그대로 사용해도 된다.


💡 인터페이스의 static 메소드, 자바 8

Default Method와 마찬가지로 Java 8부터 지원하기 시작한 기능이며 인터페이스 내에 기능을 정의하여 사용할 수 있다.

단 default method와 차이점은 인터페이스를 구현하는 구현체에서 재정의하여 사용할 수 없다는 점이다.

또한, 클래스의 static 메소드가 클래스명.메소드명 형식으로 호출하는 것과 달리 인터페이스의 static method는 인터페이스명.메소드명 형식으로 호출해야한다.

package com.livestudy.eighth;

public interface Camera {
    void shootPhoto(int time);
    void shootVideo(int time);

		//Default method
    default void shoot(){
        System.out.println("Camera is shooting");
    }

    //static method
    static void stop() {
        System.out.println("Camera has stopped");
    }
}

////

package com.livestudy.eighth;

public class PhoneApp {
    public static void main(String[] args) {
        IPhone iPhone = new IPhone();
				iPhone.shoot();
        Camera.stop();
			  iPhone.stop();   //에러
    }
}

💡 인터페이스의 private 메소드, 자바 9

default, static method로 인해 인터페이스 내부에 코드를 구현할 수 있게 되면서 외부 구현체에서 필요한 메소드가 아닌 내부에서만 작동되기를 원하는 메소드도 노출이 되는 경우가 발생했다.

따라서, 이러한 문제점을 해결하기 위해 Java 9부터는 private method를 지원하여 코드의 중복을 피하고, 내부에서 작동하는 메소드에 대해서 캡슐화를 유지할 수 있게 하였다.

package com.livestudy.eighth;

public interface Camera {
    void shootPhoto(int time);
    void shootVideo(int time);

    default void setting(){
        int timer = basicTimer();
        int memory = basicMemory();
        System.out.println(timer + "seconds");
        System.out.println("Memory "+memory+"Giga");
    }

    private static int basicTimer(){
        return 10;
    }

    private int basicMemory() {
        return 5;
    }
}

📃 Reference

인터페이스 : https://sungwoon.tistory.com/59

레퍼런스 참조변수 : https://doublesprogramming.tistory.com/157

Default Method : https://siyoon210.tistory.com/95

Static Method : https://atoz-develop.tistory.com/entry/JAVA-8-interface-default-키워드와-static-메소드

profile
백엔드 개발자 지망생입니다!

0개의 댓글