[Java] 자바 인터페이스 (interface)의 기본 문법과 꼭 지켜야 하는 규칙


인터페이스(interface)의 내부 규칙 I

 - 인터페이스는 호출 규칙을 정의하는 것이기 때문에 추상 메서드만 선언 가능하다.

 - 인터페이스는 인스턴스 변수를 가질 수 없다.

   즉 인터페이스에 선언된 모든 메서드는 public이고 abstract이다.

   또한 인터페이스에 선언된 모든 필드(영역)는 public이고 static이며 final이다.



인터페이스(interface) 예제 

public interface A {
  // 1) 규칙이기 때문에 무조건 공개 메서드이고 추상 메서드이다.
  public abstract void m1();
 
  // 2) public을 생략해도 내부적으로 public으로 간주한다.
  abstract void m2();
 
  // 3) abstract를 생략해도 내부적으로 abstract로 간주한다.
  void m3();
 
  // 4) 절대 구현 메서드를 가질 수 없다.
  // void m4() {} // 컴파일 오류!

  // [필드 선언 규칙]
  // 1) 규칙이기 때문에 무조건 공개 필드이고, 스태틱 필드이다
  //    그리고 값을 바꿀 수 없는 상수 필드이다.
 
  public static final int v1 = 200;
 
  // 2) public을 생략하면 내부적으로 public으로 간주한다.
  static final int v2 = 200;
 
  // 3) static을 새약하면 내부적으로 static으로 간주한다.
  final int v3 = 200;
 
  // 4) final을 생략하면 내부적으로 final로 간주한다.
  int v4 = 200;

}





인터페이스(interface)의 규칙 II

 - 인터페이스도 상속 받아 서브 인터페이스를 만들 수 있다.



A 인터페이스로부터 상속 받은 B 인터페이스의 예제


public interface B extends A {

  /*public static final*/ int v5 = 300;
  /*public abstract */ void m4();
}

public interface C extends A {
  /*public static final*/ int v6 = 300;
  /*public abstract */ void m5();
  /*public abstract */ void m6();
}




인터페이스(interface)의 규칙 III

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

 - 어차피 구현되지 않은 메서드이기 때문이다.



B 인터페이스와 C 인터페이스로부터 다중 상속 받은 D 인터페이스 예제

public interface D extends B, C {
  // 어차피 인터페이스의 모든 메서드는 구현된 게 아니기 때문에,
  // 서브 인터페이스에서 수퍼 인터페이스의 메서드를 다시 선언하더라도 문제는 없다.
  // => 다만 부질 없는 짓이다.
  // void m3();
 
  // 다음과 같이 메서드를 추가하는 것은 안된다.
  // 이미 같은 이름을 가지고 파라미터의 타입도 일치하는 메서드가 있는데
  // 리턴 타입만 달리해서 메서드를 선언하면
  // 나중에 크래스에서 이 메서드를 구현할 때 구분할 수 없기 때문에 이것은 불가능하다.
  // int m3(); // 컴파일 오류!
 
  // 파라미터가 다르다면 오버로딩이 가능하기 때문에 괜찮다.
  void m3(int a);
 
  // 파라미터만 다르다면 당연히 오버로딩이 가능하기 때문에 괜찮다.
  int m3(int a, int b);
 
  // B에도 m5()가 있고, C에도 m5()가 있다.
  // 그러나 어차피 구현한 메서드가 아니기 때문에 둘 중 어느 것을 가져와도 된다.
  // 그래서 인터페이스는 다중 상속을 허락한다.
 
  void m7();

}



정리!


B, C 인터페이스의 추상 메서드를 재정의 하는 MyClass 클래스 예제


public class MyClass implements B, C{

  // B와 C의 수퍼 인터페이스는 A의 메서드 구현
  public void m1() {}
  public void m2() {}
  public void m3() {}
 
  // B에 선언된 메서드 구현
  public void m4() {}
  public void m5() {}
 
  // C에 선언된 메서드 구현
  // public void m5() {} // 이미 위에서 m5()를 구현했기 때문에 다시 구현할 필요는 없다.
  public void m6() {}

}



*주의!

- 만약 'E'라는 새로운 인터페이스에서 int m5()를 선언했을 경우,

  MyClass가 E를 상속 받는다면 m5()는 리턴 타입이 다르기 

  때문에 public void m5()로는 B, C, E의 규칙을 충족시킬 수

  없다. 이 경우 컴파일 오류가 발생한다.

- 즉, 인터페이스의 메서드 리턴 타입은 모두 동일해야 한다.






블로그 이미지

필로그래머

,

[Java] 추상 클래스 (abstract class)와 추상 메서드(abstract method)의 개념


추상 클래스와 추상 메서드


 1) 추상 클래스

- 서브 클래스에게 공통 필드와 메서드를 상속해주는 용도로 사용한다.

- 그 자체로 인스턴스를 만들어 사용하지 않는 경우에

  "추상 클래스"로 선언한다.

- 예) Car 클래스는 Sedan, Truck, Bulldozer의 공통 기능을 모아둔 클래스이다.

 이 클래스를 직접 사용하려고 만든 것이 아니다.

 이와 같이 특히 generalization 과정에 생성된 수퍼 클래스인 경우

 주로 추상클래스로 선언한다.

      - 왜? 

        직접 사용하지 말라는 의미이다. 

        상속 받아 쓰라는 의미이다.

        단지 상속 해주는 역할을 한다는 의미이다.

- 실습

- Car 클래스와 Loader 클래스는 직접 사용하기 위해 만든 클래스가 이니다.

  서브 클래스의 공통 필드나 메서드를 관리하기 쉽도록 하기 위해 수퍼

  클래스로 정의한 것이다. 

  그래서 이 클래스들은 abstract로 선언하는 것이 마땅하다.



 2) 추상 메서드

- 서브 클래스 마다 기능을 다루는 방법이 다를 것 같으면

  굳이 수퍼 클래스에서 그 메서드를 정의하지 말라

- 또는 서브 클래스에게 특정 메서드를 반드시 구현하도록 강요(강제)하고 싶다면

  수퍼 클래스에서 그 메서드를 정의하지 말라!

- 이런 메서드인 경우,

   "추상 메서드"로 선언한다.

의미?

  - 서버 클래스를 만드는 개발자, 이 메서드를 반드시 구현하세요.

  - 서브 클래스를 만드는 개발자, 제가 이 메서드를 구현해 봐야 소용이 없을 것 같습니다.

  - 예) Car 클래스의 run() 메서드는 모든 서브 클래스에서 재정의 해야한다.

- 추상 메서드는 추상 클래스 만이 가질 수 있다.

  인스턴스가 생성되어 실제 동작되는 클래스는 추상 메서드를 가질 수 없다.




추상 클래스 Car와 추상 메서드 run()의 예제


public abstract class Car {
  protected String model;
  protected String maker;
  protected int cc;
  public Car() {
    super();
  }
 
  public Car(String model, String maker, int cc) {
    this.model = model;
    this.maker = maker;
    this.cc = cc;
  }
 
  // 이 메서드느 어차피 서브 클래스에서 무조건 재정의할 것이다.
  // 또는 재정의하도록 강제하고 싶다.
  // 그렇다면 여기서 코딩하지 말자
  // 단지 어떤 메서드인지 선언만 하라!
  // 추상 메서드는 구현할 수 없다.
  public abstract void run();
}


Car로부터 상속 받은 추상 클래스 Loader의 예제 
- 짐을 싣는 메서드를 구현


public abstract class Loader extends Car {
  Object storage;
  public Loader() {
    super();
  }
  public Loader(String model, String maker, int cc) {
    super(model, maker, cc);
  }
 
  // 서브 클래스에서도 그냥 사용할 수 없는 메서드라면,
  // 다음 메서드와 같이 수퍼 클래스에서 선언한다.
  public void load(Object obj) {
    this.storage = obj;
    System.out.printf("%s를 실었습니다.\n", obj);
  }

}


Loader로부터 상속 받은 일반 클래스 Sedan의 예제
- Car의 추상 메서드 run()을 오버라이딩한다. 


public class Sedan extends Loader {
  boolean auto;
  public Sedan(String model, String maker, int cc, boolean auto) {
    super(model, maker, cc);
    this.auto = auto;
  }
 
  // 상속 받은 메서드를 서브 클래스의 역할에 맞게끔 재정의하자! 오버라이딩
  public void run() {
    System.out.println("부웅~~ 부드럽게 달린다.");
  }
}



Loader로부터 상속 받은 일반 클래스 Truck의 예제
- Car의 추상메서드 run()과 Loader의 일반메서드 load()를 오버라이딩 한다.

public class Truck extends Loader {
  public Truck(String model, String maker, int cc) {
    super(model, maker, cc);
  }
  // 수퍼 클래스 Car의 run()을 오버라이딩 한다.
  public void run() {
    System.out.println("부왕~~ 힘차게 달린다.");
  }
  // 수퍼 클래스 Loader의 load()를 오버라이딩 한다.
  public void load(Object obj) {
    if (obj != null) {
      super.load(obj);
    } else {
      System.out.println("비었습니다.");
    }
  } 
  public void dump() {
    if (storage != null) {
      System.out.printf("%s를 내렸습니다.\n", this.storage);
      this.storage = null;
    } else {
      System.out.println("내릴 게 없습니다.");
    }
  }

}



* 결론!


- 추상 메서드는 추상 클래스 만이 가질 수 있다.

  인스턴스가 생성되어 실제 동작되는 클래스는 추상 메서드를 가질 수 없다.

- 추상 클래스는 어디까지나 유지보수를 하게 될 개발자에게 자식 클래스에서 

   메서드를 재정의하길 바라는 마음에서, 만들어진 문법이다.




블로그 이미지

필로그래머

,