클래스를 추상 클래스로 선언해야하는 이유는 무엇입니까?


40

구문, 추상 클래스에 적용되는 규칙을 알고 있으며 추상 클래스의 사용법을 알고 싶습니다.

추상 클래스는 직접 인스턴스화 할 수 없지만 다른 클래스에 의해 확장 될 수 있습니다

그렇게하는 것의 장점은 무엇입니까?

인터페이스와 어떻게 다른가요?

하나의 클래스가 여러 인터페이스를 구현할 수 있지만 하나의 추상 클래스 만 확장 할 수 있다는 것을 알고 있습니다. 인터페이스와 추상 클래스의 차이점 만 있습니까?

인터페이스 사용에 대해 알고 있습니다. Java에서 AWT의 이벤트 위임 모델에서 배웠습니다.

어떤 상황에서 클래스를 추상 클래스로 선언해야합니까? 그 장점은 무엇입니까?


14
이 질문을 받았습니다. 검색하면 이와 같은 다른 질문이 나타납니다. Google에서 시작하면 스택 오버플로로 이어집니다. 이 모든 것은 중복됩니다 : stackoverflow.com/search?q=abstract+interface .
S.Lott

1
"방금 논의한 추상 클래스의 사용법은 ... 규칙"입니다. 뭐? "규칙"과 "사용법"의 차이점은 무엇입니까? 그런데 당신의 정확한 질문은 물었습니다. 알아. 나는 대답했다. 계속 찾아 봐 검색 사용법을 익히는 것이 중요합니다.
S.Lott

"어떤 상황에서 클래스를 추상 클래스로 선언해야합니까? 그 이점은 무엇입니까?"
Vaibhav Jani

인터페이스는 순수한 추상 클래스입니다. 그들은 같은 것 중 하나입니다. 서브 클래스에만있는 데이터가 필요하기 때문에 구현할 수없는 기본 클래스의 함수에 항상 추상 클래스를 사용하지만 모든 서브 클래스 에이 함수가 있고 그에 따라 구현되도록하고 싶습니다.
AngryBird

템플릿 메소드 패턴이 매우 강력하고 추상 클래스에 대한 유스 케이스입니다.
m3th0dman

답변:


49

답변은 추상 클래스와 인터페이스 의 차이점 을 설명하는 데 도움 되지만 왜 선언 해야하는지 대답하지 않습니다 .

순수한 기술적 관점에서 볼 때 클래스를 추상으로 선언 할 필요 는 없습니다 .

다음 세 가지 클래스를 고려하십시오.

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

당신은하지 않습니다 이 프로그램을 작성 할 때 입력 할 수 있습니다 구현에 명백한 문제가있는 경우에도, 데이터베이스 클래스는 추상적하게 new Database()그리고 유효 할 것이다, 그러나 그것은 작업 않을 것입니다.

어쨌든, 프로그램이 SqlDatabase만들고 OracleDatabase인스턴스를 만드는 한, 여전히 다형성을 얻을 수 있습니다.

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

추상 클래스는 방지하여 상황 개선 개발자를 개발자가 있기 때문에, 기본 클래스의 인스턴스에서 없는 기능을 가진로 표시를 . 또한 컴파일 타임 안전을 제공 하므로 추상 클래스를 확장하는 모든 클래스가 최소한의 기능을 제공 할 수 있으며 상속자가 가진 스텁 메서드 (위와 같은)를 넣는 것에 대해 걱정할 필요가 없습니다. 그것들 작동시키기 위해서는 메소드를 재정의 해야 한다는 것을 마술처럼 알기 위해.

인터페이스 는 완전히 별개의 주제입니다. 인터페이스를 사용하면 객체에서 수행 할 수있는 작업을 설명 할 수 있습니다. 일반적으로 다른 구성 요소, 객체의 서비스를 사용하는 메서드, 구성 요소 등을 작성할 때 인터페이스를 사용하지만 서비스를 얻는 실제 유형의 객체는 신경 쓰지 않습니다.

다음 방법을 고려하십시오.

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

database객체가 특정 객체에서 상속 되는지 여부는 신경 쓰지 않고 addProduct메서드 가 있는지 확인하면 됩니다. 따라서이 경우 모든 클래스가 동일한 기본 클래스에서 상속되도록하는 것보다 인터페이스가 더 적합합니다.

때로는 두 가지의 조합이 아주 잘 작동합니다. 예를 들면 다음과 같습니다.

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

일부 데이터베이스는 RemoteDatabase에서 상속하여 행을 작성하기 전에 연결하는 것과 같은 일부 기능을 공유하지만 FileDatabase는를 구현하는 별도의 클래스입니다 IProductDatabase.


16

유사점

추상화에는 추상 클래스와 인터페이스가 필요합니다. 그것들은 새로운 인스턴스로 인스턴스화 할 수 없지만 제어 컨테이너의 반전이나 공장 패턴을 통해 해결할 수 있습니다.

차이

  1. 인터페이스

    • 잘 알려진 공공 계약, 유형의 능력을 정의하십시오
    • 수평 상속, 즉 상속의 첫 번째 레벨에서 분기 (예 : 데이터베이스, 텍스트 파일, XML, SOAP 등에 로깅 기능을 정의하는 ILog)를 표시 할 수 있습니다.
    • 모든 회원은 공개
    • 구현이 허용되지 않습니다
    • 상속 자식은 많은 인터페이스를 구현할 수 있습니다.
    • 타사 통합에 유용
    • 명명은 일반적으로 I로 시작합니다
  2. 추상 클래스

    • 구조, 정체성 및 기본 지원되는 행동 정의
    • 수직 상속, 즉 여러 수준에서 깊은 분기 (예 : 도메인 기반 개발의 AbstractEntity 클래스)를 표시 할 수 있습니다.
    • 회원은 공개 상태에서 비공개로 다른 가시성을 가질 수 있습니다.
    • 일부 멤버를 구현할 수 있습니다 (예 : * 리더 클래스)
    • 상속 자식은 하나의 기본 추상 클래스 만 가질 수 있습니다

간단한 구글 쿼리로 답을 찾는 것은 실제로 쉽다 .


구현이 허용하지 않는 메나는 무엇입니까? 자바 클래스는 인터페이스를 구현할 수 있습니다.
사주

@Sajuuk 그 줄은 인터페이스를 나타냅니다. 계약 구현을 인터페이스에 넣을 수 없습니다. 추상 클래스에서 계약의 기본 구현이있을 수 있습니다.
oleksii

11

인터페이스와 어떻게 다른가요?

추상 클래스에서 일부 메소드를 구현하고 나머지 클래스를 확장 클래스에서 구현하도록 강제 할 수 있습니다. 인터페이스에서 메소드를 구현할 수 없습니다. 평범한 수업을 연장 할 때 다른 사람이 무엇인가를 무시하도록 강요 할 수는 없습니다. 추상 클래스를 사용하면 가능합니다.


기본 메소드가 도입 된 Java 8에 대한 업데이트가 필요합니다.
Haakon Løtveit

8

추상 클래스는 "is a"관계를위한 것이고 인터페이스는 "can do"를위한 ​​것입니다.

추상 클래스를 사용하면 기본 동작을 추가 할 수 있으므로 프로그래머가 디자인을 따르도록 강요하면서도 모든 것을 코딩 할 필요는 없습니다.


3

추상 클래스에 대한 일부 메소드 구현과 같은 자세한 기술적 세부 사항 외에도 의미는 다음과 같습니다.

인터페이스는 공통 기능을 정의합니다-IEnumerable은이 인터페이스를 구현하는 클래스를 열거 할 수 있도록 정의합니다. 수업 자체에 대해서는 아무 것도 말하지 않습니다.

추상 (또는 기본) 클래스는 동작을 정의합니다. WebRequest는 HttpWebRequest 등과 같은 모든 하위 클래스의 공통 동작을 정의합니다. 클래스의 핵심 의미와 실제 목적은 웹 리소스에 액세스하는 것입니다.


2

위키 백과 항목 .

인터페이스와 추상 클래스의 주요 차이점은 추상 클래스가 구현 된 메소드를 제공 할 수 있다는 것입니다. 인터페이스를 사용하면 메소드 만 선언 하고 서명을 작성할 수 있습니다 . 다음은 두 개의 인터페이스를 구현하는 추상 클래스를 확장하는 클래스의 예입니다. (java)

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

이 예제에서 MyAbstractClass 는 세 가지 값을 모두 인쇄하는 공용 메소드를 제공합니다. ImpClass 에서는 MyInterface1MyInterface2 및 getValue3에서 각각 getValue1 및 getValue2를 추상 클래스에서 구현해야합니다 .

Voilà.

더 많은 측면 (인터페이스 : 공용 메소드 만, 추상 클래스 : 보호 된 추상 및 공용 추상 메소드)이 있지만 직접 읽을 수 있습니다.

마지막으로, 추상 메소드 만 제공하는 추상 클래스는 인터페이스 인 "순수한"추상 기본 클래스입니다.


2
  • 인터페이스-몇몇 클래스가 API를 공유 할 때 (메소드 이름 및 매개 변수)
  • 추상 클래스-소수의 클래스가 동일한 코드를 공유하는 경우 (구현)

다시 말해, "이 클래스들은 반드시 구현을 공유 합니까, 아니면 공통 인터페이스를 가지고 있습니까?" 라는 질문으로 시작해야합니다.

이 세 클래스가 구현을 공유해야하지만 다른 두 클래스는 API 만 공유하는 경우와 같이 답변이 혼합 된 경우 5 개 모두에 대한 인터페이스와 공통 클래스가있는 3 개에 대한 추상 클래스를 만들 수 있습니다. 암호.

구현을 공유하는 다른 방법, 예를 들어 해당 구현으로 객체를 캡슐화하는 방법 (예 : 전략 패턴)도 있습니다.


1

개발자 (아마도 자신)가 인스턴스화를 허용하지 않기를 원할 때 클래스 초록을 선언하면 작동하지 않거나 이해가되지 않기 때문입니다.

예를 들어, 다른 유형의 게임 개체가있는 게임을 생각해보십시오. 그들은 모두 기본 GameEntity클래스 에서 상속받습니다 .

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

이 클래스는 abstract인스턴스화하는 것이 의미가 없으므로 선언 되었습니다. 게임 엔터티 및 일부 특성에 대한 일부 작업을 선언하지만이 클래스에는 이러한 특성이 초기화되지 않습니다. 이 클래스는 게임 엔터티의 템플릿 역할을하지만 자체적으로 인스턴스화되어 선언 된 것은 아닙니다 abstract.

추상 클래스와 인터페이스의 사용법 차이에 대해 :

내가 알다시피, 인터페이스는 일부 언어의 단일 상속 메커니즘에 의해 제한되지 않고 다형성 동작을 얻는 방법입니다.

예를 들어 게임으로 돌아 갑시다. Enemy에서 파생 된 클래스 를 고려하십시오 GameEntity. 이 클래스에는 메소드가 attackMeFromDistance(RangedAttacker attacker)있습니다. 이 방법은 개체가 멀리서 적을 공격 할 수 있도록하기위한 것입니다.

보시다시피이 메서드는 RangedAttacker형식을 매개 변수로 사용합니다. 그러나 모든 게임 엔티티는 이미에서 상속받습니다 GameEntity. 그들은 다른 수업을 확장 할 수 없습니다.

클래스 가져 MageArcher예를 들어 있습니다. attackMeFromDistance(RangedAttacker attacker)메소드 에서 둘 다 매개 변수로 허용되도록 하고 싶지만 이미에서 파생되었습니다 GameEntity.

이를 해결하기 위해 새로운 인터페이스를 만듭니다.

interface RangedAttacker{
    public void attackFromDistance();
}

이 인터페이스를 구현하는 클래스는 attackFromDistance()메소드 를 구현해야 하므로 공격 범위가 범위가 넓어야합니다. 이것은 attackMeFromDistance메소드가이 인터페이스를 구현하는 클래스를 안전하게 수용 할 수 있음을 의미합니다 . 그래서 만들기 MageArcher해당 인터페이스 해결할 수있는 문제를 우리의 문제를 구현합니다.

나에게 이것은 인터페이스의 힘이다.

요약하면 일부 클래스의 기본 클래스를 원할 때 일반적으로 추상 클래스를 사용하지만 자체적으로 인스턴스화하는 것은 의미가 없습니다 (또는 abstract메서드 가있는 경우). 서브 클래스에 의해 구현되며,이 경우 컴파일러는 클래스를 작성하도록 강요합니다 abstract). 단일 상속 메커니즘에 의해 제한되지 않고 인터페이스를 사용하여 다형성 동작을 얻을 수 있습니다.


0
  1. 일부 클래스에 공통적 인 메소드가 너무 적을 가능성이 있습니다 (비즈니스 로직). 나머지 방법은 다릅니다. 이러한 종류의 시나리오에서는 하나의 클래스에서 모든 공통 메소드를 구현하고 나머지는 추상으로 선언 할 수 있습니다. 그런 다음 클래스를 추상으로 선언해야합니다.
  2. 때로는 클래스에 직접 객체를 만들 수 없습니다. 그런 종류의 클래스는 클래스를 추상으로 선언해야합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.