이 답변은 추상 클래스와 인터페이스 의 차이점 을 설명하는 데 도움 이 되지만 왜 선언 해야하는지 대답하지 않습니다 .
순수한 기술적 관점에서 볼 때 클래스를 추상으로 선언 할 필요 는 없습니다 .
다음 세 가지 클래스를 고려하십시오.
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
.