[자바] 인터페이스

Gammi·2022년 11월 1일
0

JAVA

목록 보기
23/35
post-thumbnail

📚 인터페이스(Interface)


  • 클래스가 아니므로 선언 시 class 키워드 사용 X

  • interface 키워드 사용하여 정의


  • 구현코드가 없음

  • 상수와 추상메서드만 가질 수 있음

    -> 모든 멤버 변수는 public static final(생략 가능)이 붙은 상수로 취급

    • final : 값 변경 불가
    • static : 클래스명만으로 접근 가능
    • public : 누구나 접근 가능

    -> 모든 메서드는 public abstract(생략 가능)가 붙은 추상 메서드로 취급

    • 메서드 바디X
    • public : 누구나 접근 가능

  • 추상메서드를 포함하므로 객체(인스턴스) 생성 X

    -> 참조변수 타입으로는 사용 가능


  • 서브클래스(구현클래스)에서 인터페이스를 상속받아 구현해야 하는 경우 extends 대신 implements 키워드 사용

< 인터페이스 정의 기본 문법 >

[접근제한자] interface 인터페이스명 {
[상수...
추상메서드...]
}


< 인터페이스를 상속받은 서브클래스 정의 문법 >

[접근제한자] class 클래스명 implements 인터페이스명 {
}


public static void main(String[] args) {
// MyInterface mi = new MyInterface();
// MyInterface.NUM1 = 99;
// 상수는 값 변경할 수 없음

// 인터페이스는 인스턴스(객체) 생성 불가능
  Sub s = new Sub();
  s.method1();
  s.method2();
  s.NUM1 = 30;
  // 상수는 값 변경할 수 없음
  
  MyInterface mi = new Sub();
  // 참조변수 타입으로 사용 가능
  mi.method1();
  mi.method2();
  // 상속된 메서드만 호출 가능함
  System.out.println(mi.NUM1);
  // 상수를 인터페이스 안에서 선언했기 때문에
  // 업캐스팅 했어도 접근 가능함
  // 하지만!!!! 상수에 접근할 때는 변수로 접근하는 것보다
  System.out.println(MyInteface.NUM1);
  // 클래스 또는 인터페이스로 접근하는 게 더 좋음!!!!
}

interface MyInterface {
  public static final int NUM1 = 10;
  // 인터페이스에서 정의되는 멤버변수는 모두 상수!!
  // 값 변경 불가능함
  int NUM2 = 20;
  // 다 상수이기 때문에
  // 앞에 public static final을 생략해도
  // 알아서 상수로 저장됨
  
// public MyInterface() {} 
// 인터페이스는 생성자 생성 불가능
  
  public abstract void method();
  // 인터페이스에서 만들어지는 메서드는 모두 추상메서드!
  // 일반메서드는 정의 불가능함
  
  void method2();
  // public abstract는 생략 가능함
}

class Sub implements MyInterface {
// 인터페이스를 상속받는 서브클래스 정의
  @Override
  public void method1() {
  // 추상메서드 구현해야함
  }
  public void method2() {
  // 아까 위에 인터페이스에서는 public을 생략했는데
  // 인터페이스 내 모든 메서드의 접근제한자는 public이므로
  // 오버라이딩을 통해 구현하는 메서드는
  // public 타입만 가능하다!
  // 그래서 public 꼭 적어줘야 함!!!!
  // 안그러면 오류남...
  }
}





📕 다이아몬드 상속


  • 다이아몬드 상속 = 다중 상속

  • 인터페이스를 상속받는 서브클래스에서 다중 상속 가능

    -> implements 키워드 뒤 복수 개의 인터페이스 지정 가능

  • 인터페이스끼리 상속받을 경우 extends로 상속

    -> 추상메서드는 구현X


🔗 참고

다중 상속을 허용했을 경우 다른 클래스 두 개를 동시에 상속 받는 서브클래스에서 공통적으로 갖는 메서드를 호출했을 때 어떤 클래스의 메서드를 호출했는지 분명하게 알 수 없는 문제가 발생하기 때문에 자바에서 다중 상속은 금지되어 있음


추상메서드를 이용한 다중상속(불가)

abstract class Animal {
  public abstract void breeding();
}

class Whale extends Animal {
  @Override
  public void breeding() {
    System.out.println("새끼를 낳아 번식!");
  }
}

class Shark extends Animal {
  @Override
  public void breeding() {
    System.out.println("알을 낳아 번식!");
  }
}

class Whaleshark extends Whale, Shark {
  Whaleshark.breeding();
  //는 Whale의 메서드인지 Shark의 메서드인지 분명하지 않음
}

인터페이스를 이용한 다중 상속(가능)

interface Animal {
  public abstract void breeding();
  // 인터페이스 내의 모든 메서드는 추상메서드임
  // 서브클래스에서 구현 필수
}

interface Swim {
}

interface Whale extends Animal, Swim {
// 인터페이스 간의  상속은 extends 사용함
}
interface Shark extends Animal, Swim {
}

class Whaleshark implements Whale, Shark {
// 인터페이스를 상속받은 서브클래스에서 추상메서드 구현
  @Override
  public void breeding() {
    System.out.prinln("알을 낳아 번식");
  }
}
  • 인터페이스 내에서는 모든 메서드가 추상메서드이므로 중복되는 메서드에 대한 구별 없이 직접 구현하면 되기 때문에 중복된 메서드로 인한 혼란이 없음

    -> 다이아몬드 상속에서의 문제점이 사라짐





✔ 인터페이스의 필요성


  • 구현의 강제로 코드의 통일성 향상 (= 표준화)

  • 인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체 용이

    -> 특정 클래스를 직접 다루는 대신 부모 인터페이스 타입으로 클래스를 다루게 되면 실제 인스턴스가 바뀌더라도 기존 코드를 수정할 필요가 없어짐

  • 서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여

    -> 다형성 확장

  • 모듈간 독립적 프로그래밍으로 인한 개발 기간 단축


인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체 용이
-> 인터페이스 사용 시 손쉬운 모듈 교체 지원

public static void main(String[] args) {
  PrintClient p = new PrintClient();
  // 인스턴스 생성
  p.setPrintClient(new LaserPrinter);
  // LaserPrinter를 Printer로 업캐스팅
  // 원래 파라미터는 Printer 타입이었기 때문에
  // set메서드 호출해서 프린터기 인스턴스를 
  // 파라미터로 전달할 경우 업캐스팅이 일어남
  p.print("Hello, World");
  
  p.setPrintClient(new DotPrinter);
  // LaserPrinter를 DotPrinter로 교체
  p.print("Hello, Java");
  }
}

class PrintClient {
  private Printer printer;
  // Printer 인터페이스 타입의 변수 선언
  
  public void setPrintClient(Printer printer) {
    this.printer = printer;
  }
  // Setter 메서드를 통해 Printer 타입 변수 초기화
  
  public void print(String fileName) {
    printer.print(fileName);
    // Printer 타입 변수에 저장된 프린터의 인스턴스 사용해서
    // print() 메서드 호출 가능
    // PrinterClient의 멤버변수이기도 하지만
    // interface Printer의 참조변수이기도 하니까!
  }
}

interface Printer {
// 제일 먼저 정의해야 함
  public abstract void print(String fileName);
}

class LaserPrinter implements Printer {
  @Override
  public void print(String fileName) {
    System.out.println("LaserPrinter로 " + fileName + " 출력하기!"); 
  }
}

class DotPrinter implements Printer {
  @Override
  public void print(String fileName) {
    System.out.println("DotPrinter로 " + fileName + " 출력하기!"); 
  }
}

서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여

public static void main(String[] args) {
  Chargeable c = new NoteBookPc();
  // 참조변수 형태로 접근
  c.charge();
  Chargeable c2 = new SmartPhone();
  c2.charge();
  // 바로 업캐스팅 진행해서 접근 가능
  
  Chargeable[] chargeArr = {
  	new NoteBookPc(),
    new SmartPhone()
  };
  // Chargeable 타입 배열로도 관리 가능
  // 자동 업캐스팅
  for(int i = 0; i < chargeArr.length; i++) {
    chargeArr[i].charge();
  }
}

interface Chargeable {
  public abstract void charge();
}

class Pc {}
class HandPhone{}

class NoteBookPc extends Pc implements Chargeable {
  @Override
  public void charge() {
    System.out.println("노트북 충전 중");
  }
}

class SmartPhone extends HandPhone implements Chargeable {
  @Override
  public void charge() {
    System.out.println("스마트폰 충전 중");
  }
}
profile
개발자가 되었어요⭐️

0개의 댓글