接口和抽象类之间到底有什么区别?


当前回答

抽象类和接口的一般思想是由使用这些通用“设置”(某种模板)的其他类(不能单独构建)来扩展/实现,这使得为以后扩展它的所有对象设置特定的通用行为变得简单。

抽象类具有常规方法集和抽象方法。扩展类在被抽象类扩展后可以包括未设置的方法。当设置抽象方法时,它们由稍后扩展抽象方法的类定义。

接口与抽象类具有相同的财产,但只包含抽象方法,这些方法可以在其他类中实现(可以是多个要实现的接口),这就创建了一个更持久的方法/静态变量定义。与抽象类不同,您不能添加自定义的“常规”方法。

其他回答

总结起来最简单的方法是:

完全抽象,除了默认和静态方法;虽然它有默认和静态方法的定义(方法签名+实现),但它只有其他方法的声明(方法签名)。受比类更宽松的规则约束(类可以实现多个接口,接口可以从多个接口继承)。所有变量都是隐式常量,无论是否指定为公共静态final。所有成员都是隐式公共的,无论是否指定为公共成员。通常用于保证实现类将具有指定的特性和/或与实现相同接口的任何其他类兼容。

同时,抽象类是:

从完全抽象到完全实现,具有一个或多个抽象方法的倾向。可以包含声明和定义,声明标记为抽象。一个完整的类,并且受制于管理其他类的规则(只能从一个类继承),条件是它不能实例化(因为不能保证它完全实现)。可以具有非常量成员变量。可以实现成员访问控制,将成员限制为受保护、私有或私有包(未指定)。通常用于提供可由多个子类共享的尽可能多的实现,或者提供程序员能够提供的尽可能的实现。

或者,如果我们想将其归结为一句话:接口是实现类所拥有的,而抽象类是子类所拥有。

代表实际实现的抽象类和接口之间的差异。

接口:它是一个关键字,用于定义对象的模板或蓝图,它强制所有子类遵循相同的原型,作为实现,所有子类都可以根据其要求自由实现功能。

我们应该使用接口的一些其他用例。

两个外部对象(应用程序中的第三方集成)之间的通信通过此处的接口完成。

抽象类:抽象,它是一个关键字,当我们在任何类之前使用这个关键字时,它就成为抽象类。它主要用于我们需要定义模板以及所有子类后面的对象的一些默认功能时,这样它就删除了多余的代码和一个可以使用抽象类的用例,例如,我们不希望其他类可以直接实例化该类的对象,只有派生类可以使用该功能。

抽象类示例:

 public abstract class DesireCar
  {

 //It is an abstract method that defines the prototype.
     public abstract void Color();

  // It is a default implementation of a Wheel method as all the desire cars have the same no. of wheels.   
 // and hence no need to define this in all the sub classes in this way it saves the code duplicasy     

  public void Wheel() {          

               Console.WriteLine("Car has four wheel");
                }
           }


    **Here is the sub classes:**

     public class DesireCar1 : DesireCar
        {
            public override void Color()
            {
                Console.WriteLine("This is a red color Desire car");
            }
        }

        public class DesireCar2 : DesireCar
        {
            public override void Color()
            {
                Console.WriteLine("This is a red white Desire car");
            }
        }

接口示例:

  public interface IShape
        {
          // Defines the prototype(template) 
            void Draw();
        }


  // All the sub classes follow the same template but implementation can be different.

    public class Circle : IShape
    {
        public void Draw()
        {
            Console.WriteLine("This is a Circle");
        }
    }

    public class Rectangle : IShape
    {
        public void Draw()
        {
            Console.WriteLine("This is a Rectangle");
        }
    }

为了给出一个简单但明确的答案,设置上下文是有帮助的:当您不想提供完整的实现时,可以同时使用这两种方法。

因此,主要的区别是接口根本没有实现(只有没有主体的方法),而抽象类也可以有具有主体的成员和方法,即可以部分实现。

在一个接口中,所有方法都只能是定义,而不能实现单个方法。

但在抽象类中必须有一个只有定义的抽象方法,但其他方法也可以在抽象类的实现中。。。

许多初级开发人员错误地将接口、抽象类和具体类视为同一事物的细微变化,并纯粹基于技术原因选择其中之一:我需要多重继承吗?我需要一些地方来放置常用方法吗?除了一堂具体的课,我还需要为别的事情而烦恼吗?这是错误的,隐藏在这些问题中的是主要问题:“我”。当你自己编写代码时,你很少想到其他现在或将来的开发人员正在处理或使用你的代码。

接口和抽象类,虽然从技术角度看很相似,但有着完全不同的含义和目的。

总结

接口定义了某个实现将为您实现的契约。抽象类提供了您的实现可以重用的默认行为。

备选摘要

接口用于定义公共API抽象类用于内部使用和定义SPIs

隐藏实现细节的重要性

具体的类以非常具体的方式完成实际工作。例如,ArrayList使用一个连续的内存区域以紧凑的方式存储对象列表,这提供了快速的随机访问、迭代和就地更改,但在插入、删除和偶尔甚至添加时非常糟糕;同时,LinkedList使用双链接节点来存储对象列表,这提供了快速迭代、就地更改和插入/删除/添加,但在随机访问时非常糟糕。这两种列表针对不同的用例进行了优化,如何使用它们非常重要。当您试图从与您密切交互的列表中挤出性能时,当选择列表类型时,您应该仔细选择要实例化的列表。

另一方面,列表的高级用户并不真正关心它是如何实现的,他们应该与这些细节隔离开来。让我们想象一下,Java没有公开List接口,但只有一个具体的List类,这实际上就是LinkedList现在的样子。所有Java开发人员都会定制自己的代码以适应实现细节:避免随机访问,添加缓存以加快访问速度,或者自己重新实现ArrayList,尽管这与所有其他仅适用于List的代码不兼容。那太可怕了。。。但现在想象一下,Java主控实际上意识到链接列表对于大多数实际用例来说都是可怕的,并决定切换到数组列表,以获得唯一可用的list类。这将影响世界上每一个Java程序的性能,人们对此不会感到高兴。主要原因是实现细节是可用的,而开发人员认为这些细节是他们可以依赖的永久契约。这就是为什么隐藏实现细节,只定义抽象契约很重要。这就是接口的目的:定义一个方法接受什么样的输入,以及期望什么样的输出,而不暴露所有可能诱使程序员调整代码以适应未来任何更新可能改变的内部细节的勇气。

抽象类位于接口和具体类之间。它应该帮助实现共享公共或无聊的代码。例如,AbstractCollection提供了基于大小为0的isEmpty的基本实现,包含作为迭代和比较,addAll作为重复添加等等。这让实现专注于区分它们的关键部分:如何实际存储和检索数据。

API与SPI

接口是代码不同部分之间的低内聚网关。它们允许图书馆存在和发展,而不会在内部发生变化时破坏每个图书馆用户。它叫做应用程序编程接口,而不是应用程序编程类。在较小的规模上,它们还允许多个开发人员在大型项目上成功协作,通过文档化的界面将不同的模块分离。

抽象类是在实现接口时使用的高内聚性帮助程序,假定实现细节级别。或者,抽象类用于定义SPI、服务提供商接口。

API和SPI之间的区别很细微,但很重要:对于API,重点是谁使用它,而对于SPI,重点是由谁实现它。

向API添加方法很容易,API的所有现有用户仍将编译。向SPI添加方法很难,因为每个服务提供商(具体实现)都必须实现新方法。如果使用接口定义SPI,则每当SPI合约发生变化时,提供商都必须发布新版本。如果改用抽象类,则可以根据现有的抽象方法定义新方法,或者将新方法定义为空抛出未实现的异常存根,这至少允许旧版本的服务实现仍然编译和运行。

关于Java 8和默认方法的说明

虽然Java8引入了接口的默认方法,这使得接口和抽象类之间的界限更加模糊,但这并不是为了实现可以重用代码,而是为了更容易地更改同时用作API和SPI的接口(或者错误地用于定义SPI而不是抽象类)。

使用哪一个?

这个东西应该由代码的其他部分或其他外部代码公开使用吗?向其添加一个接口,以从公共抽象契约中隐藏实现细节,这是该事物的一般行为。这件事是不是应该有多个实现,有很多共同的代码?既要做一个接口,又要做一个抽象的、不完整的实现。是否只有一个实现,而没有其他人会使用它?让它成为一个具体的课程。“ever”是一个很长的时间,你可以安全地使用它,并在它上面添加一个界面。

一个推论:另一种方法往往是错误的:当使用一个东西时,总是尝试使用您实际需要的最通用的类/接口。换句话说,不要将变量声明为ArrayListtheList=newArrayList(),除非您实际上对它是一个数组列表有很强的依赖性,并且没有其他类型的列表可以为您删除它。如果它是一个列表,而不是任何其他类型的集合这一事实实际上无关紧要,请改用ListtheList=newArrayList,甚至使用CollectiontheCollection=newArrayNist。