PostIT

[Java] Interface와 Abstract class의 차이점 및 사용방법 이해하기. 본문

Java

[Java] Interface와 Abstract class의 차이점 및 사용방법 이해하기.

shun10114 2017. 4. 18. 09:41

[Java] Interface와 Abstract class의 차이점 및 사용방법 이해하기.


- 최근 수정일 : 2017.04.18


- IDE : Spring Tool Suite

- OS : OSX Sierra (10.12.3)

- Lang : JAVA 1.8



## 1. 배경

    • 웹 프로젝트를 진행하면 Service와 Repository(Dao) 그 외 다양하게 interface를 구현하여 사용한다. 이는 Service Layer와 Repository Layer계층에서 필요한 메소드(CRUD) 로직을 다른 클래스와 공통적으로 구현할 수 있게 Interface로 구현을 강제시키는 것과 같다라는 결론에 이르렀고, 책을 보며 응집도와 결합도에 대한 합리적인 패턴과 같다라는 것을 알게 되었습니다.
    • 특히, Repository Class는 Interface를 구현시키고, (추상)클래스를 상속받아 사용하는 경우가 많습니다. Mybatis와 JPA(Hibernate)는 공통적으로 Session을 통해 Query 혹은 Persistance를 통해 DB와 지속적으로 통신을 주고받아야 하기 때문입니다. 그리고 필요한 부분은 추상 메소드를 통해 재정의하여 사용할 수 있기 때문입니다.
    • 하지만, 정확하게 얘기해서 Absctract Class와 interface의 차이를 이해하고있지 못해 두 차이점을 공부하여 블로그에 남겨야겠다는 생각을 했습니다.

## 2. 키워드


- 객체지향 설계원칙(SOLID)
1) SRP : 단일 책임 원칙
2) OCP : 개방 폐쇄 원칙
3) LSP : 리스코프 치환원칙
4) ISP : 인터페이스 분리원칙
5) DIP 의존관계 역전원칙

      • 개방 폐쇄 원칙
        이는 스프링의 교과서 토비의 스프링에서 나오듯이, 클래스 간의 '높은 응집도와 낮은 결합도'라는 문장을 대표하는 것이라고 할 수 있다.
        - 응집도 : 변화가 일어날 때, 해당 모듈에서 변하는 부분이 큰 정도.
        - 결합도 : 하나의 오브젝트가 변경이 일어날 때, 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도.

      • 확장에 대해 열려 있다.
        이것은 모듈의 동작을 확장할 수 있다는 것을 의미한다. 애플리케이션의 요구 사항이 변경될 때, 이 변경에 맞게 새로운 동작을 추가해 모듈을 확장할 수 있다. 즉, 모듈이 하는 일을 변경할 수 있다.
      • 수정에 대해 닫혀 있다.
        모듈의 소스 코드나 바이너리 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경할 수 있다. 그 모듈의 실행 가능한 바이너리 형태나 링크 가능한 라이브러리(예를 들어 윈도의 DLL이나 자바의 .jar)를 건드릴 필요가 없다.


## 3. 내용

    • 추상클래스
      추상클래스를 설명하자면 없거나 하나 이상의 추상메소드를 가지고 있는 것이 추상클래스다.
      추상메소드는 추상메소드는 안이 아직 구현되어 있지 않은 abstract로 정의된 메소드를 말한다.

public abstract class AbstractExample extends AbstractExample2{

/** 일반 메소드 */

protected void normalMethod(String queryId) {

System.out.println("나 일반 메소드야.");

}

/** 추상 메소드 */

public abstract void abstractMethod(String queryId);

}


public abstract class AbstractExample2 {

/** 추상 메소드 */

public abstract void abstractAnotherMethod(String queryId);

}

public class NormalClass extends AbstractExample2{

    /** 일반 메소드 */

    protected void normalMethod(String queryId) {

        System.out.println("나는 일반 메소드야.");

    }


    @Override

    public void abstractAnotherMethod(String queryId) {

        System.out.println("나는 상속받은 추상 클레스의 추상 메소드야!!");

    }

}

      1. 클래스안의 메소드가 단 한개라도 추상메소드가 있다면 그 클래스 앞에는 반드시 abstract 클래스명으로 표기되어야 하며 abstract와 final 키워드를 동시에 표기할수 없다. 
      2. 추상클래스는 일반적인 메소드도 있을수 있고 추상메소드가 있을수도 있다. 추상클래스는 일반변수들을 가질수 있다. 추상클래스도 인터페이스처럼 추상클래스가 아닌 클래스에서 상속을 받는다면 추상메소드가 있을경우 모두 구현해주어야 한다. 물론 추상클래스에서 추상클래스를 상속받는다면 모두 구현하지 않아도 된다. 추상클래스에서도 인터페이스를 구현할수 있는데 이때는 구현하지 않고 그냥 놔둘수 있다. 
      3. 추상클래스는 생성자를 가질수 있다. 추상클래스는 인스턴스를 만들수 없지만 추상클래스를 상속받은 클래스를 통하면 인스턴스화가 가능하다. 예를 들자면 "NormalClass ab = new NormalClass( )" 이런 형태가 될것이다. 추상클래스의 접근지정자는 어떤 것이나 가능하다. 추상클래스의 궁극적인 목적은 상속하기 위함이다. 어떤 클래스가 추상클래스를 상속받을때는 extends를 쓴다.

    • 인터페이스
      인터페이스는 Body가 비어있는 메소드들의 형태들만 써놓은 것이며 상속하는 클래스들에서 해당 메소드들의 내용을 구현해서 사용해야하는 메소드들의 집합이라 할수있다.(==추상메소드)

public interface CommonAnimal {

void run();

}


public class Horse implements CommonAnimal {

private String name;

private String color;


@Override

public void run() {

            System.out.println("네 발로 뛴다.");

}


public String getName() {

    return name;

}


public void setName(String name) {

    this.name = name;

}


public String getColor() {

            return color;

}


public void setColor(String color) {

    this.color = color;

}

}

      1. 인터페이스에 새로운 메소드를 추가한다고 하고 그 아래에 인터페이스로부터 상속되는 클래스가 있다면 새로운 메소드에 대한 내용을 반드시 구현해야한다. 인터페이스안의 모든 메소드들은 추상메소드이다.
      2. 인터페이스는 final을 붙일수 없고 인터페이스 변수들은 static이어야만 한다. 한마디로 인터페이스는 일반변수들을 가질수 없다. 
        (4번의 내용으로 인해 일반변수를 가질 수 없다.
      3. 인터페이스는 생성자를 가질수 없다. 인터페이스는 인스턴스를 만들수 없지만 인터페이스를 구현한 클래스를 통하면 인스턴스화가 가능하다. 예를 들자면 "CommonAnimal ca = new Horse( )" 이런 형태가 될것이다. 
      4. 인터페이스의 접근지정자는 아예 없거나 public이거나 아님 abstract만 가능하다. 어떤 클래스가 인터페이스를 상속받을때는(구현할때는) implements를 쓴다. 물론 클래스가 다른 클래스를 상속하면서 인터페이스를 구현하는 것도 가능하다. 그럴때는 extends 쓰고 implements를 순서대로 쓰면 된다.
      5. 인터페이스는 하나 이상의 인터페이스들을 상속할수 있는데 여러개일때는 콤마(,)를 사용하며 이때는 클래스에서 상속받는게 아니라 인터페이스에서 상속받으니 즉 내용이 없는 메소드들을 그대로 두는것이니 구현이 아니라 인터페이스간의 상속이므로 일반 클래스끼리 상속할때처럼 extends를 쓴다. 

## 4. 결론


- Abstract Class와 Interface 용도

public abstract class CommonAnimal {
private String name;
private String color;
/* Abstract method */
abstract void run();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}
}

public interface RideInterface {
/* Interface (Abstract method) */
void youCanRide(String riderName);
}

public class Horse extends CommonAnimal implements RideInterface {
@Override
public void run() {
System.out.println(this.getName()+"은(는) 네 발로 뛴다.");
}

@Override
public void youCanRide(String riderName) {
System.out.println(this.getName()+"은(는) "+riderName+"을(를) 태울 수 있다.");
}
}

public class MainClass {
public static void main(String[] args) {
/* Instence created */
Horse horse=new Horse();
/* 상속받은 동물 속성 */
horse.setName("얼룩말");
horse.setColor("흰색 검은색 줄무늬");

/* 추상 */
System.out.println(horse.getName());
System.out.println(horse.getColor());
horse.youCanRide("hooney");

/* 결과 
얼룩말
흰색 검은색 줄무늬
얼룩말은(는) 네 발로 뛴다.
얼룩말은(는) hooney를 태울 수 있다.
*/
}
}
    • 추상클래스는 객체의 추상적인 상위 개념으로 공통된 개념을 표현할 때 쓴다. 위에 예를 들어, 동물에는 공통적으로 이름과 색깔이 있고, 달린다는 공통된 행동이 있다. 하지만, 각기 요구하는 객체구현 방식마다 객체가 달라지겠지만, 공통된 속성을 동물이라는 추상클래스에 추상메소드로 구현하여 하위 개념인 말이라는 객체에 상속시켜 구현하면 객체의 재사용성과 객체를 표현하는 것이 더 명확해진다고 생각한다. 그리고 동물마다 차이가 생기는 태울 수(탈 수) 있는가, 없는가를 나누기 위해 RideInterface를 구분하여 동물 중에서 태울 수 있는 동물들에게 Implements 시키면 객체를 좀더 잘 표현할 수 있을거라고 생각한다.
    • 특히, 인터페이스는 구성하는 요소들이 자주 바뀔때 쓰면 유용하다. 그리고, 메소드 형태만 서로 공유해서 구현(재정의)되는 상황일 때 사용하는 것이 적합하다. 즉, 공통된 메소드(객체행위)로 만들어 사용하고 싶을때 인터페이스를 이용하면 효과적이다. 
    • 또 다른 이유는, 동시개발이 가능하므로 시간을 많이 단축시킬수 있다는 점이다. 인터페이스안의 메소드들은 내용이 없는 상태이나 메소드에 대한 결과값은 내용을 만들지 않아도 미리 알수 있으므로 인터페이스의 내용을 누군가가 구현하고 있는 동안에 다른 한사람은 그 메소드가 구현되고 나면 작동할 결과값으로 같은 시간에 다른 일을 할 수 있으니 개발작업이 한층더 빨라질수 있는 것입니다. 
    • 또한 여러사람이 인터페이스를 통해 그런 작업을 동시에 한다고 가정했을때, 인터페이스안의 메소드 내용을 변경하더라도 그를 구현한 모든 클래스들을 변경할 필요없이 해당 메소드의 구현되는 내용만 변경하면 모든 처리가 가능해지므로 '높은 응집도와 낮은 결합도'를 해결할 수 있습니다.


- 차후에 위에 언급된 객체지향의 요소들과 접근 제한자에 대해서 공부하도록 하겠습니다.



## 5. 참조


 - http://alecture.blogspot.kr/2011/05/abstract-class-interface.html?m=1












Comments