Java의 정석 - 추상 클래스와 인터페이스

원태연·2022년 5월 30일
0

Java의 정석

목록 보기
9/19
post-thumbnail

추상화

abstract라는 제어자를 붙혀 추상화 메서드, 클래스를 선언 할 수 있다,

abstract class Person{
  abstract int walkTo();  //{} 구현부는 작성하지 않는다
}

추상 메서드를 가지는 클래스는 반드시 추상 클래스이다.

ERROR : class Person{ abstract int walkTo()}

추상 클래스는 직접 호출,참조할 수 없고, 상속을 통해서 완성을 해야한다. 또, 추상 클래스에서 선언된 추상 메서드는 이를 상속받은 클래스에서 오버라이딩을 통해 모두 완성되어야 한다.

장점

abstract class Person{
    int x;
    abstract void moveTo(int x);
}
class PersonWithWalk extends Person{
    void moveTo(int x){
        System.out.println("Required Time : " + x);
    }
}
class PersonWithBike extends Person{
    void moveTo(int x){
        System.out.println("Required Time : " + x/2);
    }
}
class PersonWithSkate extends Person{
    void moveTo(int x){
        System.out.println("Required Time : " + x/3);
    }
}

Person의 추상화를 통해 moveTo()라는 메서드를 자손들이 필요하게 가공하여 사용한다.

추상화가 아니더라도 오버라이딩을 통해 자손들이 필요에 맞게 가공할 순 있다.

하지만, 상속을 통해서 결정되는 Person 에서의 불필요한 구현을 피하고, Person이 참조되는 실수를 방지할 수 있다. 또, Person 을 상속받는 자식들은 반드시 moveTo()를 구현하도록 강요되기 때문에 어떤 규칙으로서의 역할도 가능하다.

public class playGround {
    public static void play() {
        Person[] group = {new PersonWithWalk(), new PersonWithBike(), new PersonWithSkate()};
        for(int i = 0; i < 3; i++){
            group[i].moveTo(100);
         
        }
    }
}
//Required Time : 100
//Required Time : 50
//Required Time : 33

인터페이스

인터페이스란 추상메서드의 집합이다. 인스턴스 변수는 존재 할 수 없다.

interface Walkable{
  public static final walkpace = 4;
  public abstract void walkTo();
  void stop(); // == public abstract stop();
}

인터페이스의 메서드는 모두 public abstract 생략이 가능하다.

상속

인터페이스 간의 상속이 가능하며, 다중 상속도 가능하다

다중상속이 가능한 이유는, 부모들의 메서드 충돌 위험이 존재하지 않기 때문이다

인터페이스의 메서드는 구현부가 없음을 보장할 수 있기에 같은 이름의 메서드를 상속받아도 문제 되지 않는다

interface Walkable{};
interface Runable{};
interface RunningMember extends Walkable, Runable{};

구현

추상메서드의 집합인 인터페이스를 사용하려면 이를 사용하여 구현하여야 한다.

interface Walkable{
	void moveTo(int d);
};
interface Singable{
	void moveTo(int d);
};
class Person implements Walkable, Sing{
	public void moveTo(int d){
    System.out.println("Move to "+d);
  }
}

이때 주의할 점이 있다. Person클래스에서의 moveTo는 인터페이스의 오버라이딩이므로 접근제어자의 범위가 더 넓어야 한다. 인터페이스에서는 public abstract가 생략되었으므로 오버라이딩을 하기 위해선 public을 꼭 붙혀야 한다.

다형성

package playground;
interface Walkable{
    void moveTo(int d);
};
interface Jumpable{
    void JumpTo(int h);
};
class Person implements Walkable, Jumpable{
    public void moveTo(int d){
        System.out.println("Move to "+d);
    }
    public void JumpTo(int h){
        System.out.println("Jump to "+h);
    }
}
public class playGround {
    public static void play() {
        Person me = new Person();
        me.moveTo(10);                   //Move to 10
        me.JumpTo(20);                   //Jump to 10
        Walkable meWalk = new Person();  //Move to 10
        meWalk.moveTo(10);
//      meWalk.JumpTo(10);   ERROR
        Jumpable meJump = new Person();  //Jump to 10
//      meJump.moveTo(10);	 ERROR
        meJump.JumpTo(10);
    }
}

인터페이스의 이유

인터페이스의 존재는 굉장히 유용하다

  • 독립적인 프로그래밍이 가능하다

    클래스의 선언과 구현을 분리하여 다른 클래스들 간의 의존을 낮추어 변경이 용이해진다.

    package playground;
    interface Printer{
        void printMessage();
    }
    
    /* 직접 연결 된 경우 (A와 연결)
    class NewPrinter{
        public void printMessage(Printer A){
            A.printMessage();
        };
    }
    */
    class NewPrinter{
        public void printMessage(Printer i){
            i.printMessage();
        };
    }
    class PrinterA implements Printer{
        public void printMessage() {
            System.out.println("Here is A");
        }
    }
    class PrinterB implements Printer{
        public void printMessage() {
            System.out.println("Here is B");
        }
    }
    
    public class playGround {
        public static void play() {
            NewPrinter printer = new NewPrinter();
            printer.printMessage(new PrinterA()); //Here is A
            printer.printMessage(new PrinterB()); //Here is B
        }
    }
    
    // class newPrinter가 인터페이스 타입을 받아 printMessage()메서드를 사용함으로써,
    // PrinterA와 PrinerB와의 관계가 느슨해졌다.
    // printer.printMessage(new PrinterB()); 와 같은 변경사항에도 인터페이스를 통해 유연하게 작동한다
  • 표준화가 가능하다

    프로토콜 처럼 어떤 규칙으로서의 역할이 가능하다.

    package playground;
    interface Calculator{
        int add(int a, int b);
        int substract(int a, int b);
        int mutiple(int a, int b);
        int divide(int a, int b);
    }
    //ERROR
    class CalculatorA implements Calculator{
        public int add(int a, int b) {
            return a + b;
        }
        public int substract(int a, int b) {
            return a - b;
        }
        public int mutiple(int a, int b) {
            return a * b;
        }
    }
    // ERROR 
    // divide() 메소드가 존재하지 않아 컴파일 오류가 표기된다
    public class playGround {
        public static void play() {
    
        }
    }
  • 서로 관계없은 클래스들을 맺을 수 있다.

    package playground;
    interface Repairable {
        void repair();
    }
    class Unit{
        String name;
    }
    class GroundUnit extends Unit implements Repairable {
        void walking(){};
        public void repair() {};
    }
    class FlyingUnit extends Unit implements Repairable {
        void flying(){};
        public void repair(){};
    }
    
    public class playGround {
        public static void play() {
            Repairable flyingUnit = new FlyingUnit();
            Repairable groundUnit = new GroundUnit();
            Repairable[] repairUnits = {flyingUnit, groundUnit};
          //상속관계가 아닌 FlyingUnit과 GroundUnit을 Repairable이라는 관계로 묶을 수 있다.
        }
    }
profile
앞으로 넘어지기

0개의 댓글