【问题标题】:What's the point in having an abstract class with no abstract methods?有一个没有抽象方法的抽象类有什么意义?
【发布时间】:2026-01-27 01:50:01
【问题描述】:

可以有一个抽象类实现它的所有方法——其中没有抽象方法。

例如:

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。

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

编辑:我能想到的另一种用途:

它可能正在扩展另一个抽象类或实现一个接口而不实现该类的抽象方法——尽管它正在实现它自己的所有方法。不惜一切代价。

【问题讨论】:

  • 您可能希望提供任何人实例化此类。
  • 如果你将构造函数设为私有,那么你将把它变成一个单例,或者需要一个公共静态方法来获取类的实例,其中抽象类可以帮助你继承......
  • 使所有构造函数protected(注意默认的)具有非常相似的效果,实际上是我首选的这样做方式。也许这是 OP 中的一个错字。

标签: java inheritance abstract-class


【解决方案1】:
  • 你可以声明实现一个接口而不提供实现,然后每个孩子隐式地获得接口扩展

  • 您阻止创建此类的实例

  • 您将来为所有孩子提供通用实施

【讨论】:

    【解决方案2】:

    它有一个概念意义:这个类的行为本身没有意义。

    当然,如果没有明确定义的扩展点(即抽象方法),很难想象这样的场景,但有时它会是您问题的合理准确模型。

    你可以有这样的东西:

    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 版),这不会像抽象类解决方案那样优雅且明显更好,因为您必须以类型安全为代价。

    【讨论】:

    • 请举个例子。我无法想象它什么时候有用
    • 在允许的情况下接受第一个 ok ans。另一个获得赞成票。
    【解决方案3】:

    如果您认为它是一个实用程序类,它会很有用。

    【讨论】:

      【解决方案4】:

      正如您所指出的,您可以通过将其构造函数设为私有来防止该类被实例化。除此之外,没有任何好处。这可能只是为了提供语言完整性而受到支持。

      【讨论】:

        【解决方案5】:

        实际的区别是您不能创建它的实例。您必须对其进行子类化并创建子类的实例。

        至于你为什么要这样做,在实践中......我很难想出一个好的理由。你可以说这个类只有在有人创建了一个实现某些功能的子类时才有意义。但是为什么不在超类中使该函数抽象呢?

        我不排除有人可能会提出一些有意义的例子,但我想不出一个。仅仅因为可以编写一段代码并且代码编译成功并不意味着它是有意义的。我的意思是,我可以写“total_price = item_price * zip_code + customer_height_in_cubits - 7.879”,但这并不意味着这样的代码行有意义。

        【讨论】:

          【解决方案6】:

          假设您不关心抽象类的方法是实现的还是抽象的,但设计上它必须是抽象的,这样当有人扩展它时,他们必须添加更多方法或覆盖现有的方法或按原样使用。如果他们不想覆盖这些方法,那么该抽象类中已经提供了默认行为。

          在这个抽象类中,您强制执行的唯一标准是 - 根本无法实例化该类,并且他们必须在使用它之前拥有唯一的类版本。

          因此,一般来说,实现了很少或所有方法的抽象类比没有实现任何方法的接口要好得多。这是基于您将其用作单一继承的假设。

          【讨论】:

            【解决方案7】:

            我们一般使用Abstraction concept with inheritance

            如果这些陈述中的任何一个适用于 你的情况:

            • 您希望在几个密切相关的类之间共享代码。

            回答你的问题,

            Why declare a class with concrete methods Abstract?

            一个可能的原因是支持继承而不实际创建对象


            假设你有两个类,一个是 Abstract,另一个是 Concrete

            抽象类AbsClass

            abstract class AbsClass {
              int a = 5;
            
              //Constructor
              public AbsClass() {
                System.out.println(a);
              }
            
              void methodA() {
                System.out.println(a + 10);
            
              }
            
            }
            

            具体类具体类

            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
            
            
            }
            

            【讨论】:

              【解决方案8】:

              考虑类似于NVI pattern 的东西(不确定你在 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,您只公开了一个不能被覆盖的公共 final 方法。它在那里执行一些必需的工作和一个可选的方法。扩展这个类时,只能覆盖doOptional()。

              调用 B.doSomething() 将始终在继续之前打印“required”。

              由于 doOptional() 不是抽象的,所以 A 类需要是抽象的并没有纯粹的代码原因。但您的特定项目可能需要它。例如,始终扩展到特定子项目的基础服务。

              【讨论】:

                【解决方案9】:

                这对于从抽象基类派生的类必须具有一些彼此不同的行为这种行为不能抽象为驻留在对所有类具有相同签名的方法中。如果不同的行为需要传递不同原始类型的方法,则可能会出现无法共享签名的情况。因为它们使用原始类型,所以您不能使用泛型来表达相似性。

                没有任何抽象方法的抽象基类的行为有点像标记接口,因为它声明实现类必须提供某些行为,而无需将该行为封装在具有所有实现都相同的签名。当实现类具有一些共同行为时,您将使用抽象基类而不是标记接口,特别是如果基类可以为派生类实现它。

                例如:

                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();
                    }
                 }
                

                【讨论】:

                  最近更新 更多