ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [whiteship] 온라인스터디 - 8주차.인터페이스
    IT 발자취.../JAVA 2021. 1. 9. 22:40

     

    github.com/whiteship/live-study/issues/8

     

    8주자 과제: 인터페이스 · Issue #8 · whiteship/live-study

    목표 자바의 인터페이스에 대해 학습하세요. 학습할 것 (필수) 인터페이스 정의하는 방법 인터페이스 구현하는 방법 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법 인터페이스 상속 인터

    github.com

    목표

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

    학습할 것 (필수)

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

     

    인터페이스란 ?

    - 추상클래스보다 추상성이 더 심화된 개념이다.

     

    - 추상클래스를 부분적으로만 완성된 '미완성 설계도'라고 한다면, 인터페이스는 구현된 것은 아무 것도 없고 밑그림만 그려져 있는 '기본 설계도'라고 할 수 있다.

     

    * 추상클래스보다 더욱 강력한 추상화를 제공하는 도구이고, 그를 통해 다형성을 더욱 강력하게 해주는 도구이다.

     

    * 상수나 추상메소드 이외에 따로 정의되어있는 것이 없어서 (JDK 1.8이전 기준 ) 다중 상송의 문제 없이 비슷한 효과를 낼 수 있다.

     

    주요한 예는 java Collection과 이를 구현하는 다양한 자료구조들이 인터페이스로 되어있다.

    List는 그 Interface를 구현한 클래스이다.

    인터페이스 정의하는 방법

    인터페이스를 작성하는 것은 클래스를 작성하는 것과 같다. 

    interface [인터페이스이름] extends [부모 인터페이스명 ...] {
        // 상수 (static final)
        public static final 타입 상수이름 = 값;
        // static final은 생략될 수 있다.
        
        // 추상 메소드 (public abstract)
        public abstract 메서드이름(매개변수목록);
        // public abstract 는 생략될 수 있다.
    }

    일반적인 클래스의 멤버들과 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.

    - 인터페이스는 다중 상속이 가능한 것으로 볼 수 있다. (extedns 키워드 사용 )

    - 상수와 추상메소드를 가질 수 있다.

    - 모든 멤버변수는 public static final이어야하며, 이를 생략할 수 있다.
    - 모든 메서드는 public abstract이어야하며, 이를 생략할 수 있다.
    단, static 메서드는 디폴트 메서드는 예외 ( JDK 1.8부터 변경 )

     

    인터페이스 구현하는 방법

    인터페이스도 추상클래스처럼 그 자체로는 인터페이스를 생성할 수 없으며, 추상클래스가 상속을 통해 추상메서드를 완성하는 것처럼, 인터페이스도 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 하는데, 그 방법은 추상클래스가 자신을 상속받는 클래스를 정의하는 것과 다르지 않다. 다만 클래스는 확장한다는 의미의 키워드 'extends'를 사용하지만 인터페이스는 구현한다는 의미를 키워드 'implements'를 사용할 뿐이다.

    class 클래스이름 implements 인터페이스이름 {
    	// 인터페이스에 정의된 추상메서드를 구현해야 한다.
    }

    implements 키워드 사용

    interface Flyable {
        void fly();
    }
    interface Speakable {
        void speak();
    }
    
    class Bird implements Flyable, Speakable {
    
        @Override
        public void fly() {
            System.out.println("Bird's Flying");
        }
    
        @Override
        public void speak() {
            System.out.println("Bird's Speaking");
        }
    }

    여러개의 인터페이스를 구현 가능하다. 

    인터페이스에서 추상 메소드 시점에 내용이 결정되는 것이 아니라, 구현 시점에 어떤 내용이 들어갈지 결정되므로 이름이 같아도 상관 없다. ( 다중 상속 문제가 없다 )

     

    익명 구현 객체

    Flyable fly = new Flyable() {
            @Override
            public void fly() {
            System.out.println("Bird Anonymous fly!");
        }
    };

    익명 객체를 활용하면 인터페이스 자체만으로 구현할 수 있다.

    이렇게 쓰는 경우 익명 객체를 만들어 변수 fly에 주입 시켜준다.

    * java의 인터페이스에는 생성자가 없으므로, 항상 flyable() 의 괄호는 빈 상태이다.

    * 단순히 인터페이스의 구현역할을 하는 익명 객체일 뿐, 상속이나 또다른 인터페이스를 구현하는 것은 불가능하다.

    * expression일종이므로 뒤에 세미콜론을 붙이는 것을 잊지 말아야 한다.

     

     

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

    다형성의 특징을 활용하여 인터페이스를 타입으로 사용할 수 있다.

    public class Week8MainTest {
        public static void main(String[] args) {
            List<Flyable> flyList = List.of(new Bird(), new Airplane(), new Drone());
            flyList.forEach(Flyable::fly);
        }
    }
    
    class Airplane implements Flyable {
        @Override
        public void fly() {
            System.out.println("Airplane fly");
        }
    }
    
    class Drone implements Flyable {
    
        @Override
        public void fly() {
            System.out.println("Drone fly");
        }
    }
    
    class Bird implements Flyable, Speakable {
    
        @Override
        public void fly() {
            System.out.println("Bird's Flying");
        }
    
        @Override
        public void speak() {
            System.out.println("Bird's Speaking");
        }
    }

    flyable을 통해서 bird를 선언하게되면, Flyable가 가질  수 있는 메소드만 사용할 수 있게 된다.

    (speak 메소드를 사용할 수 없게 된다 )

     

    이는, 자바의 다형성의 대표적인 예시로 활용된다.

    인터페이스 상속

    인터페이스는 상속이 가능하다.

    interface [인터페이스 이름] extends [부모 인터페이스명 ...] {
    
    }

    extends 키워드를 사용하여 인터페이스도 여러개의 계층 구조를 가질 수 있다.

    public interface Flyable {
        void fly();
    }
    
    interface Landable extends Flyable {
        void land();
    }
    
    class Machine implements Landable, Flyable {
    
        @Override
        public void land() {
            
        }
    
        @Override
        public void fly() {
    
        }
    }

    인터페이스 자식은 상속된 상위 인터페이스가 가지고 있는 메서드까지 모두 구현해야 한다.

     

    인터페이스의 다중 상속

    추상메소드의 경우 추상메소드 선언 시점에 결정되는 것이 아니라, 구현 시점에 결정되기 때문에, 인터페이스의 상속에서도 역시 다중상속 문제가 없다.

     

    interface Playable {
        void doAction();
    }
    interface Stopable {
        void doAction();
    }
    
    interface Performable extends Playable, Stopable {
    }
    
    class Audio implements Performable {
    
        @Override
        public void doAction() {
            System.out.println("Play and Stop");
        }
    }

     

    주의해야할 상황

    컴파일러가 혼돈할 수 있는 경우 에러가 나온다. 예제 - 리턴타입만 다를 경우

    interface Playable {
        void doAction();
    }
    interface Stopable {
        boolean doAction();
    }
    
    interface Performable extends Playable, Stopable {
        // 'doAction()' in 'week8.Stopable' clashes with 'doAction()' in 'week8.Playable'; methods have unrelated return types
    }

     

    함수형 인터페이스, 자바8

    자바8에서는 함수를 '1급 시민'처럼 다룰 수 있도록, 함수형 인터페이스를 제공한다.

    1급 시민이 되면, 함수는

    • 변수에 값을 할당할 수 있다
    • 함수를 파라미터로 넘겨줄 수 있다.
    • 함수의 반환값이 될 수 있다.

    함수형 인터페이스는 아래와 같이 사용한다.

    @FunctionalInterface
    public interface Addition {
        int addition(final int num1, final int num2);
    }
    • 한개의 추상 메소드만 가져야 한다.
    • @FuntionalInterface 어노테이션을 붙여야 한다.
    • 이렇게 되면 Addition 타입을 가지는 addtion이라는 메소드는 이제부터 기존에 썼던 1급 시민처럼 사용할 수 있다.

    1. 변수에 값을 할당

    public class Week8MainTest {
        public static void main(String[] args) {
            Addition add = (i1, i2) -> i1 + i2;
            System.out.println(add.addition(1, 2));
        }
    }

     

    2. 함수를 파라미터로 넘겨줌

    public class Week8MainTest {
        public static void main(String[] args) {
            test((i1, i2) -> i1 + i2);
        }
        static void test(Addition addition) {
            System.out.println(addition.addition(1, 2));
        }
    }

    3. 함수의 반환값이 될 수 있어야 한다.

    public class Week8MainTest {
        public static void main(String[] args) {
            System.out.println(returnAddition().addition(1, 2));
        }
        static Addition returnAddition() {
            return (i1, i2) -> i1 + i2;
        }
    }

     

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

    인터페이스는 추상성을 강화한 개념이었는데, 이제 구현체까지 제공할 수 있게 되었다.

    다만, 원래 기능보다는 extension한다는 느낌이 있다.

    따라 사용상에 몇가지 주의사항을 아는게 중요하다.

     

    default

    default 키워드는 "default implemention"이란 의미로서, 인터페이스 내부에서 바로 구현하는 키워드이다.

    interface Stopable {
        // 추상 메서드
        void doAction();
        // default 메서드
        default void beforeStop() {
            System.out.println("print: stop actions");
        }
    }
    class Audio implements Performable {
    
        @Override
        public void doAction() {
            System.out.println("Play and Stop");
        }
    
        Audio() {
            beforeStop();
        }
    
        @Override
        public void beforeStop() {
            System.out.println("print : override default method!!");
        }
    }

    default implementation은 다시 오버라이딩 될 수 있다.

     

    마찬 가지로 다른 인터페이스에 의해 오버라이드 될 수 있다.

    interface Performable extends Playable, Stoppable {
        @Override
        default void beforeStop() {
            System.out.println("print : Overridden another interface");
        }
    }

    이때 자식 인터페이스에서 다시 추상메소드로 오버라이드 재정의할 수 있다.

     

    interface Performable extends Playable, Stoppable {
        void beforeStop();
    }
    

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

    해당 인터페이스를 구현한 모든 인스턴스, 헬퍼, 유틸리티 메소드를 제공할 수 있다.

    static 메소드는 클래스의 static 메소드처럼 사용된다.

    * static 메소드는 재정의 불가능

    interface interfaceMethodsExample {
        // 추상 메서드
        void abstractMethod();
        // default 메서드
        default void defaultMethod() {
            System.out.println("print: stop actions");
        }
        // static 메서드
        static void staticMethod() {
            System.out.println("print : static method");
        }
    }

    * 인터페이스명.staticMethod()로 호출해야한다.

     

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

    Java9은 인터페이스를 더욱 클래스처럼 쓸수 있게 만들어주는 키워드를 추가하였다.

    인터페이스의 모든 메소드들은 무조건 public 이어야하는데, java9부터는 interface 내부에 private 을 만들어서 외부에 공개하지 않으면서 코드 중복을 피할 수 있게 되었다.

     

    interface Calculator {
        private int plus(int num1, int num2) {
            return num1 + num2;
        }
    
        private static int multi(int num1, int num2) {
            return num1 * num2;
        }
        default int secretCalculate() {
            return plus(1, 2) + multi(1, 2);
        }
    }

    오류 케이스

    // 상속 케이스
    interface Calculatable extends Calculator {
        default int secretCalculate() {
            // ERROR !!'multi(int, int)' has private access in 'week8.Calculator'
            return multi(1,2);
        }
    }
    
    // 구현 케이스
    class CalculatorImpl implements Calculator {
        CalculatorImpl() {
            System.out.println(secretCalculate());
            // ERROR !! plus(int, int)' has private access in 'week8.Calculator'
            plus(1,2);
        }
    }

     

     

    스터디중 너무 정리 잘하는 분의 블로그가 있어 참고하였습니다.

    www.notion.so/Java-8-0cc8c251d5374ac882a4f22fa07c4e6a

    댓글

Designed by Gintire