簡體   English   中英

有一個沒有抽象方法的抽象類有什么意義?

[英]What's the point in having an abstract class with no abstract methods?

可以有一個實現所有方法的抽象類-里面沒有抽象方法。

例如。:

public abstract class someClass { 
    int a; 
    public someClass (int a) { this.a = a; } 
    public void m1 () { /* do something */ } 
    private void m2 () { /* do something else */ }  
}

與擁有與具體類相同的類相比,擁有這樣的抽象類(如果有)有什么優勢?

我能想到的是,當我將其聲明為抽象時,它不會被實例化。 但是,我可以通過將其具體化並將其構造函數設為私有來達到相同的效果。

TIA。

// ==================

編輯:我可以想到的另一種用途:

它可能正在擴展另一個抽象類或實現一個接口,而不實現該類的抽象方法-盡管它正在實現自己的所有方法。 不管它的價值。

  • 您可以聲明要實現一個接口而不提供實現,然后每個子對象都隱式地獲得接口擴展

  • 您可以防止創建此類的實例

  • 您將來會為所有孩子提供共同的實現

它具有概念上的含義:此類的行為本身沒有任何意義。

誠然,很難想象沒有明確定義的擴展點(即抽象方法)的情況,但有時這將是問題的合理模型。

你可以有這樣的事情:

public abstract class ObjectWithId {
    private final String id;

    public ObjectWithId( String id ) {
       this.id = id;
    }

    public final String getId() {
       return id;
    }
}

然后,您可以擴展它以聲明具有ID的不同類型的對象。 在這里,您具有完全指定和實現的行為,但對子類可能沒有表現出任何其他行為的限制。

請注意,雖然建模相同事物的一種更整潔的方法是使用合成而不是繼承。

public final class ObjectWithId<T> {
    private final String id;
    private final T ob;

    public ObjectWithId( String id, T ob ) {
       this.id = id;
       this.ob = ob;
    }

    public String getId() {
       return id;
    }

    public T getObject() {
       return ob;
    }
}

但是在引入泛型之前(在Java版本1.4之前),這並沒有那么優雅,而且顯然比抽象類解決方案要好,因為您必須在類型安全性方面進行交易。

如您所指出的,可以通過將其構造函數設為私有來防止實例化該類。 除此之外,沒有任何好處。 可能只是為了提供語言完整性而支持此功能。

我們通常將抽象概念與繼承一起使用

如果以下任何一種情況適用於您的情況,請考慮使用抽象類:

  • 您想在幾個緊密相關的類之間共享代碼。

為了回答您的問題,

Why declare a class with concrete methods Abstract?

一種可能的原因是在不實際創建對象的情況下支持繼承


假設您有兩個類別,一個是“抽象”類別,另一個是“其他”類別

抽象類AbsClass

abstract class AbsClass {
  int a = 5;

  //Constructor
  public AbsClass() {
    System.out.println(a);
  }

  void methodA() {
    System.out.println(a + 10);

  }

}

具體類ConcreteClass

class ConcreteClass {
  int a = 10;

 //Made the constructor Private to prevent from creating objects of this class
  private ConcreteClass() {
    System.out.println(a);

  }

  void methodA() {
    System.out.println(a + 10);
  }

}

上面的兩個類的功能應類似(?),直到嘗試對它們進行子類化為止

class AbsImplementer extends AbsClass {
//Works fine

}

class ConcImplementer extends ConcreteClass {
//Compilation Error Implicit super constructor ConcreteClass() is not visible


}

如果您將其視為實用程序類 ,則可能會很有用。

實際的區別在於您無法創建它的實例。 您必須將其子類化並創建該子類的實例。

至於為什么要在實踐中這樣做呢……我很難想到一個很好的理由。 您可以說該類僅在有人創建實現某些功能的子類時才有意義。 但是,為什么不使該函數在超類中抽象化呢?

我不排除有人提出一個有意義的例子的可能性,但我想不出一個例子。 僅僅因為可以編寫一段代碼並且該代碼可以成功編譯並不意味着就可以了。 我的意思是,我可以寫“ total_price = item_price * zip_code + customer_height_in_cubits-7.879”,但這並不意味着這樣的代碼行將有意義。

好吧,假設您不在乎抽象類的方法是實現的還是抽象的,但是通過設計,它必須是抽象的,以便當有人擴展它時,他們必須添加更多方法或重寫現有方法或按原樣使用。 如果他們不想覆蓋這些方法,則該抽象類中已經提供了默認行為。

在這個抽象類中,您強制執行的唯一條件是-一個人無法實例化該類,並且在使用它之前必須擁有其唯一的版本。

因此,總的來說,幾乎沒有實現任何方法的抽象類比擁有根本沒有實現任何方法的接口要好得多。 這是基於您將其用作單個繼承的假設。

考慮一下類似於NVI模式的東西(不確定在Java中將其稱為什么):

public abstract class A {
    public final void doSomething() {
        System.out.println("required");
        doOptional();
    }

    protected void doOptional() {
        System.out.println("optional");
    }
}

public class B extends A {
    @Override
    protected void doOptional() {
        System.out.println("overridden");
    }
}

對於您的公共API,您只公開了不能覆蓋的public final方法。 它在其中執行一些必需的工作以及一個可選方法。 擴展此類時,只能覆蓋doOptional()。

調用B.doSomething()始終會在繼續之前打印“ required”。

由於doOptional()不是抽象的,所以沒有純粹的代碼原因需要將類A抽象。 但是對於您的特定項目可能是理想的。 例如,基本服務始終擴展到特定的子項目中。

當從抽象基類派生的類必須具有某些彼此不同的行為, 該行為不能抽象為駐留在對所有類具有相同簽名的方法中時,這可能很有用。 如果不同的行為需要傳遞不同的原始類型的方法,則可能無法共享簽名。 因為它們使用原始類型,所以不能使用泛型來表達相似性。

沒有任何抽象方法的抽象基類的行為有點像標記接口 ,因為它聲明實現類必須提供某種行為,而不能將該行為封裝在新方法中,而該方法的簽名對於所有實現都是相同的。 當實現類具有某些共同的行為時,您將使用抽象基類而不是標記接口,尤其是如果基類可以為派生類實現它時。

例如:

abstract class Sender {
   protected final void beginMessage() {
      ...
   }

   protected final void endMessage() {
      ...
   }

   protected final void appendToMessage(int x) {
      ...
   }
 }

 final class LongSender extends Sender {
    public void send(int a, int b, int c) {
       beginMessage();
       appendToMessage(a);
       appendToMessage(b);
       appendToMessage(c);
       endMessage();
    }
 }

 final class ShortSender extends Sender {
    public void send(int a) {
       beginMessage();
       appendToMessage(a);
       endMessage();
    }
 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM