拥有一个没有抽象方法的抽象类有什么意义?

时间:2014-09-05 19:33:55

标签: java inheritance abstract-class

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

例如:

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。

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

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

它可能正在扩展另一个抽象类或实现一个接口而不实现该类的抽象方法 - 尽管它正在实现它自己的所有方法。无论它值多少钱。

9 个答案:

答案 0 :(得分:2)

  • 您可以声明实现一个接口而不提供实现,然后每个子项隐式获得扩展接口

  • 您阻止创建此类的实例

  • 您将来为所有孩子提供共同的实施

答案 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),这并不像抽象类解决方案那样优雅且明显更好,因为您必须在类型安全方面进行交易。

答案 2 :(得分:2)

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

答案 3 :(得分:2)

我们通常使用Abstraction concept with inheritance

  

如果任何这些语句适用,请考虑使用抽象类   你的情况:

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

要回答你的问题,

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


}

答案 4 :(得分:1)

如果您将其视为实用程序类,则非常有用。

答案 5 :(得分:1)

实际差异在于您无法创建它的实例。您必须对其进行子类化并创建子类的实例。

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

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

答案 6 :(得分:1)

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

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

因此,通常,实现少数或所有方法的抽象类比具有根本没有实现方法的接口要好得多。这是基于您将其用作单个继承的假设。

答案 7 :(得分:1)

考虑与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,您只公开一个无法覆盖的公共最终方法。它在那里执行一些必要的工作和一个可选的方法。扩展此类时,只能覆盖doOptional()。

调用B.doSomething()将始终打印&#34; required&#34;在它继续之前。

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

答案 8 :(得分:1)

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

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

例如:

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