프로그래밍 공부

객체지향 프로그래밍(인터페이스) 본문

Programming/JAVA

객체지향 프로그래밍(인터페이스)

khj1999 2024. 10. 8. 08:14

인터페이스

  • 자바에서 클래스가 반드시 구현해야 하는 메소드의 청사진을 제공하는 일종의 계약

    • 이렇게 보면 설명이 어려운데 쉽게 생각하면 게임기의 버튼을 생각하면 될 것 같다.
      같은 버튼이라도 어떤 게임인지에 따라 기능이 다르기 떄문
  • 인터페이스는 메소드 선언만을 포함하며, 메소드의 구현은 하지 않음

  • 이를 통해 클래스가 특정 기능을 반드시 구현하도록 강제할 수 있다.

  • 자바에서 인터페이스는 다중 상속을 가능하게 하는 주요 도구

  • 특징

    • 메소드 선언만 포함: 인터페이스는 메소드의 시그니처(이름, 반환형, 매개변수)만을 선언하며, 메소드 본체는 포함하지 않음.
    • 다중 구현 가능: 클래스는 여러 개의 인터페이스를 구현할 수 있어 다중 상속이 가능.
    • 상수만 포함: 인터페이스는 필드를 가질 수 있지만, public static final로 선언된 상수만 가질 수 있다.
    • 추상 메소드와 기본 메소드: 자바 8 이후로는 인터페이스에 추상 메소드뿐만 아니라, 디폴트 메소드(기본 구현이 있는 메소드)와 정적 메소드를 포함할 수 있음.
  • 사용 목적

    • 다형성 구현: 인터페이스를 통해 다형성을 구현할 수 있으며, 같은 인터페이스를 구현하는 클래스들은 동일한 메소드들을 갖게 되어 동일한 방식으로 처리될 수 있음.
    • 구현 강제: 인터페이스는 특정 클래스가 반드시 구현해야 하는 메소드들을 명시하여
      클래스가 특정 기능을 제공하도록 강제할 수 있다.
    • 유연한 설계: 인터페이스를 사용하면 코드의 유연성과 재사용성을 높일 수 있음.
      구현체가 변경되더라도 인터페이스는 그대로 유지될 수 있기 때문에, 코드의 수정이 최소화됨.
    • 다중 상속: 클래스는 여러 인터페이스를 구현할 수 있어, 다중 상속의 효과를 얻을 수 있다.
  • 추상 클래스와의 차이점

    • 목적: 추상 클래스는 클래스들의 공통된 속성과 메소드를 정의하는 데 사용되고
      인터페이스는 클래스가 구현해야 할 계약(메소드 집합)을 정의하는 데 사용.
    • 구현 여부: 추상 클래스는 일반 메소드를 포함할 수 있지만, 인터페이스는 기본적으로 메소드의 선언만
      포함(자바 8 이후 디폴트 메소드와 정적 메소드가 추가됨).
    • 다중 상속: 인터페이스는 다중 상속을 지원하지만, 추상 클래스는 단일 상속만 지원.
  • 인터페이스 예제 코드

      interface Animal {
          // 추상 메소드 (구현이 필요함)
          void sound();
    
          // 디폴트 메소드 (기본 구현 제공)
          default void sleep() {
              System.out.println("This animal is sleeping.");
          }
    
          // 정적 메소드 (인스턴스화 없이 호출 가능)
          static void info() {
              System.out.println("This is an Animal interface.");
          }
      }
    
      class Dog implements Animal {
          @Override
          public void sound() {
              System.out.println("Bark");
          }
      }
    
      class Cat implements Animal {
          @Override
          public void sound() {
              System.out.println("Meow");
          }
      }
    
      public class Main {
          public static void main(String[] args) {
              Dog dog = new Dog();
              dog.sound();  // "Bark" 출력
              dog.sleep();  // "This animal is sleeping." 출력 (디폴트 메소드)
    
              Cat cat = new Cat();
              cat.sound();  // "Meow" 출력
              cat.sleep();  // "This animal is sleeping." 출력 (디폴트 메소드)
    
              // 인터페이스의 정적 메소드 호출
              Animal.info(); // "This is an Animal interface." 출력
          }
      }
  • 다중 상속

    • 인터페이스를 통한 다중 구현(다중 상속과 유사) 예시

        // 첫 번째 인터페이스
        interface Flyable {
            void fly();
        }
      
        // 두 번째 인터페이스
        interface Swimmable {
            void swim();
        }
      
        // 클래스가 두 인터페이스를 구현 (다중 상속과 유사한 효과)
        class Duck implements Flyable, Swimmable {
            @Override
            public void fly() {
                System.out.println("The duck is flying.");
            }
      
            @Override
            public void swim() {
                System.out.println("The duck is swimming.");
            }
        }
      
        public class Main {
            public static void main(String[] args) {
                Duck duck = new Duck();
      
                // Flyable 인터페이스의 메소드 호출
                duck.fly();  // 출력: The duck is flying.
      
                // Swimmable 인터페이스의 메소드 호출
                duck.swim(); // 출력: The duck is swimming.
            }
        }
      
    • 효과

      • 코드 재사용성: Duck 클래스는 FlyableSwimmable 인터페이스를 통해 비행과 수영 기능을 모두 재사용할 수 있다.
      • 유연성: 클래스가 여러 인터페이스를 구현할 수 있으므로, 다양한 기능을 독립적으로 구현하고 조합할 수 있음.
      • 다형성: 인터페이스 타입으로 객체를 참조할 수 있어, 다형성을 활용한 유연한 코드 작성이 가능.
  • 인터페이스를 사용해 임시 구현후 실제 구현으로 교체하는 예시

    • 이런 인터페이스가 있을때

      interface Algorithm {
        public int run(int n1, n2);
      }
    • 임시 구현 사용

      public class DummyAlgorithm implements Algorithm {
        @Override
        public int run(int n1, int n2){
            return n1 + n2;
        }
      }
      
      public class Main {
        public static void main(String[] args){
            Algorithm cal = new DummyAlgorithm();
            System.out.println(cal.run(10, 20)); // 출력 30
        }
    • 나중에 실제 구현으로 교체하고 싶은 경우

      public class RealAlgorithm implements Algorithm {
        @Override
        public int run(int n1, int n2){
            return n1 * n2;
        }
      }
      
      public class Main {
        public static void main(String[] args){
            Algorithm cal = new RealAlgorithm();
            System.out.println(cal.run(10, 20)); // 출력 200
        }
    • 쉽게 설명하면 소통하는 두 클래스 간의 통신 규약을 구현

  • 인터페이스는 확장(상속)이 가능함

    • 다만 확장한 모든 인터페이스의 메서드를 구현해야함

        interface inter1{
            public void method1();
        }
      
        interface inter2 extends inter1{
            public void method2();
        }
      
        // 이런식으로 상속받은 모든 언터페이스를 구현해야함
        class FullInter implements inter2{
            public void method1(){}
            public void method2(){}
        }
    • 구현시 모든걸 구현하고 싶지 않으면 추상메서드로 확장하면 됨
      추상 클래스는 인터페이스에 정의되어 있는 메소드들의 일부만 구현 할 수 있고 허용이 된다
      하지만 추상클래스를 상속받는 구상 클래스는 모든것을 구현해야함

        interface inter1{
            public void method1();
        }
      
        interface inter2 extends inter1{
            public void method2();
        }
      
        // 이런식으로 추상 클래스로 선언하면 구현하고 싶은 부분만 작성해도 됨
        abstract class AbstractInter implements inter2{
            public void method1(){}
        }
      
        // 다만 실제 사용할때는 전부 구현해야함
        class FullInter extends AbstractInter{
            public void method1(){}
            public void method2(){}
        }
  • 디폴트 메서드

    • 오버라이드 가능
    • 일반적으로 인터페이스를 연장할 때 사용
    • 만약 인터페이스를 상속받는 클래스가 여러개 있을때 인터페이스를 수정하면 모든 함수가 에러 발생
      그런 오류를 방지하기 위해 디폴트를 사용한 후 기본적 구현을 제공
    • 협업시 타인의 코드를 망가뜨리지 않을 수 있음
Comments