Java中的抽象类与接口

Lea*_*ner 79 java abstract-class design-patterns interface

我被问到一个问题,我想在这里查看我的答案.

问:在哪种情况下扩展抽象类而不是实现接口更合适?

答:如果我们使用模板方法设计模式.

我对么 ?

如果我无法清楚地陈述问题,我很抱歉.
我知道抽象类和接口之间的基本区别.

1)当需求需要在特定操作的每个子类中实现相同的功能(实现方法)和其他一些操作的不同功能(仅方法签名)时,使用抽象类

2)如果需要将签名设置为相同(并且实现不同),请使用接口,以便您可以遵守接口实现

3)我们可以扩展一个抽象类的最大值,但可以实现多个接口

重申一个问题:除了上面提到的那些之外,还有其他任何场景,具体我们需要使用抽象类(一个是模板方法设计模式在概念上仅基于此)吗?

接口与抽象类

在这两者之间做出选择真的取决于你想做什么,但幸运的是,对我们来说,Erich Gamma可以帮助我们一点.

一如既往地存在权衡,接口为您提供基类的自由,抽象类使您可以自由地在以后添加新方法. - Erich Gamma

无法在不改变代码中的许多其他东西的情况下更改接口,因此避免这种情况的唯一方法是创建一个全新的接口,这可能并不总是一件好事.

Abstract classes应主要用于密切相关的对象.Interfaces更好地为不相关的类提供通用功能.

Div*_*ert 79

何时使用接口

界面允许某人从头开始实现您的界面或在其他原始或主要用途与您的界面完全不同的其他代码中实现您的界面.对他们而言,您的界面只是偶然的,必须添加到他们的代码才能使用您的包.缺点是界面中的每个方法都必须是公共的.您可能不想暴露所有内容.

何时使用抽象类

相比之下,抽象类提供了更多结构.它通常定义一些默认实现,并提供一些对完整实现有用的工具.问题是,使用它的代码必须使用您的类作为基础.如果其他想要使用您的包的程序员已经独立开发了自己的类层次结构,那么这可能非常不方便.在Java中,类只能从一个基类继承.

何时使用两者

您可以提供两全其美,界面和抽象类.如果他们选择,实现者可以忽略您的抽象类.这样做的唯一缺点是通过其接口名称调用方法比通过其抽象类名称调用方法稍慢.

  • 你是对的.但是在SO中,我们通过适当的解释回答是/否. (4认同)
  • @shiplu.mokadd.im 我不明白这怎么能让你误报他的问题。 (2认同)

Jér*_*nge 29

重申一个问题:除了上面提到的这些还有其他任何一种情况,具体我们需要使用抽象类(一种是看模板方法设计模式在概念上仅基于此)

是的,如果你使用JAXB.它不喜欢接口.您应该使用抽象类或使用泛型来解决此限制.

来自个人博客文章:

接口:

  1. 一个类可以实现多个接口
  2. 接口根本无法提供任何代码
  3. 接口只能定义公共静态最终常量
  4. 接口无法定义实例变量
  5. 添加新方法会对实现类产生连锁反应(设计维护)
  6. JAXB无法处理接口
  7. 接口不能扩展或实现抽象类
  8. 所有接口方法都是公共的

通常,接口应该用于定义合同(要实现的目标,而不是如何实现).

抽象类:

  1. 一个类最多可以扩展一个抽象类
  2. 抽象类可以包含代码
  3. 抽象类可以定义静态和实例常量(最终)
  4. 抽象类可以定义实例变量
  5. 对现有抽象类代码的修改会对扩展类产生连锁反应(实现维护)
  6. 向抽象类添加新方法对扩展类没有连锁反应
  7. 抽象类可以实现接口
  8. 抽象类可以实现私有和受保护的方法

抽象类应该用于(部分)实现.它们可以成为限制API合同实施方式的一种手段.

  • 在Java 8中,对于接口#8,您也可以使用`default`和`static`方法. (2认同)

SMK*_*SMK 14

当您拥有所有类具有相同结构但完全具有不同功能的场景时,将使用接口.

当您拥有所有类具有相同结构但具有相同且不同功能的场景时,将使用抽象类.

看一下这篇文章:http://shoaibmk.blogspot.com/2011/09/abstract-class-is-class-which-cannot-be.html


Ada*_*hes 13

这里有很多很好的答案,但我经常发现同时使用接口和抽象类是最好的方法。 考虑这个人为的例子:

您是一家投资银行的软件开发人员,需要构建一个向市场下订单的系统。您的界面捕捉了交易系统功能的最一般概念,

1) Trading system places orders
2) Trading system receives acknowledgements
Run Code Online (Sandbox Code Playgroud)

并且可以在界面中捕获, ITradeSystem

public interface ITradeSystem{

     public void placeOrder(IOrder order);
     public void ackOrder(IOrder order);

}
Run Code Online (Sandbox Code Playgroud)

现在,在销售台和其他业务线工作的工程师可以开始与您的系统交互,为他们现有的应用程序添加下订单功能。你甚至还没有开始建造!这就是接口的力量。

因此,您继续为股票交易者构建系统;他们听说您的系统具有查找廉价股票的功能,并且非常想尝试一下!您在一个名为 的方法中捕获了这种行为findGoodDeals(),但也意识到连接到市场涉及很多混乱的东西。例如,您必须打开一个SocketChannel,

public class StockTradeSystem implements ITradeSystem{    

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }
Run Code Online (Sandbox Code Playgroud)

具体的实现将有很多这些杂乱的方法,例如connectToMarket(),但是findGoodDeals()交易者真正关心的都是这些方法。

现在这里是抽象类发挥作用的地方。 您的老板通知您,货币交易员也想使用您的系统。看看货币市场,你会发现管道几乎与股票市场相同。事实上,connectToMarket()可以逐字重用以连接到外汇市场。然而,findGoodDeals()在货币领域,这是一个截然不同的概念。所以在你把代码库传给大洋彼岸的外汇奇才之前,你首先重构为一个abstract类,留下findGoodDeals()未实现的

public abstract class ABCTradeSystem implements ITradeSystem{    

    public abstract void findGoodDeals();

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }
Run Code Online (Sandbox Code Playgroud)

您的股票交易系统findGoodDeals()按照您已经定义的方式实施,

public class StockTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }
Run Code Online (Sandbox Code Playgroud)

但是现在外汇神童可以通过简单地提供findGoodDeals()货币的实现来构建她的系统;她不必重新实现套接字连接甚至接口方法!

public class CurrencyTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       ccys = <Genius stuff to find undervalued currencies>
       System.out.println("The best FX spot rates are: " + ccys);
    }
Run Code Online (Sandbox Code Playgroud)

接口编程功能强大,但类似的应用程序通常以几乎相同的方式重新实现方法。使用抽象类避免重新实现,同时保留接口的功能。

注意:人们可能想知道为什么findGreatDeals()它不是界面的一部分。请记住,该接口定义了交易系统的最通用组件。另一位工程师可能会开发一个完全不同的交易系统,他们不关心找到好的交易。该接口保证销售台也可以连接到他们的系统,因此最好不要将您的接口与应用程序概念(如“特价”)纠缠在一起。


San*_*nth 6

你应该使用哪个,抽象类或接口?

如果以下任何语句适用于您的用例,请考虑使用抽象类:

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

您希望扩展抽象类的类具有许多通用方法或字段,或者需要除 public 之外的访问修饰符(例如 protected 和 private)。

您想声明非静态或非最终字段。这使您能够定义可以访问和修改它们所属对象状态的方法。

如果以下任何语句适用于您的用例,请考虑使用接口:

您希望不相关的类会实现您的接口。例如,接口 Comparable 和 Cloneable 由许多不相关的类实现。

您想要指定特定数据类型的行为,但不关心谁实现其行为。

您想利用类型的多重继承。

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html


Rav*_*abu 5

过去三年里,随着与 Java 8 版本交互的新功能的增加,情况发生了很大变化。

\n

来自接口上的 oracle 文档页面

\n
\n

接口是一种引用类型,类似于类,只能包含常量、方法签名、默认方法、静态方法和嵌套类型。方法体仅存在于默认方法和静态方法中。

\n
\n

正如您在问题中引用的那样,抽象类最适合必须创建骨架的模板方法模式。这里不能使用接口。

\n

选择抽象类而不是接口还有一个考虑因素:

\n

您在基类中没有实现,只有子类必须定义自己的实现。您需要抽象类而不是接口,因为您想与子类共享状态。

\n

抽象类在相关类之间建立“是”关系,接口在不相关类之间提供“是”能力

\n
\n

关于问题的第二部分,这对于大多数编程语言都有效,包括java-8发布之前的 java

\n
\n

一如既往,存在一个权衡,接口为您提供了基类的自由,抽象类为您提供了稍后添加新方法的自由。\xe2\x80\x93 埃里希伽玛

\n

您可以\xe2\x80\x99t 去更改接口,而无需更改代码中的许多其他内容

\n
\n

如果考虑到上述两个因素,您更喜欢抽象类而不是接口,那么您现在必须重新考虑,因为默认方法为接口添加了强大的功能。

\n
\n

默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

\n
\n

要在接口和抽象类之间选择其中之一,oracle 文档页面引用了以下内容:

\n
\n

抽象类与接口类似。您无法实例化它们,并且它们可能包含声明有或没有实现的方法的混合。但是,使用抽象类,您可以声明非静态和最终的字段,并定义公共、受保护和私有具体方法。

\n

对于接口,所有字段都自动是公共的、静态的和最终的,并且您声明或定义的所有方法(作为默认方法)都是公共的。此外,您只能扩展一个类,无论它是否是抽象的,而您可以实现任意数量的接口。

\n
\n

请参阅这些相关问题了解更多详细信息:

\n

接口与抽象类(一般 OO)

\n

我应该如何解释接口和抽象类之间的区别?

\n

总结:现在天平更多地向接口倾斜

\n
\n

除了上面提到的那些之外,是否还有其他场景需要使用抽象类(其中之一是模板方法设计模式在概念上仅基于此)?

\n
\n

除了模板方法模式之外,一些设计模式还使用抽象类(通过接口)。

\n

创作模式:

\n

抽象工厂模式

\n

结构模式:

\n

装饰器模式

\n

行为模式:

\n

中介者模式

\n


use*_*421 3

你不正确。有很多场景。只是不可能将其简化为单个 8 字规则。