在我的一次采访中,我被要求解释接口类和抽象类之间的区别。

以下是我的回答:

Methods of a Java interface are implicitly abstract and cannot have implementations. A Java abstract class can have instance methods that implements a default behaviour. Variables declared in a Java interface are by default final. An abstract class may contain non-final variables. Members of a Java interface are public by default. A Java abstract class can have the usual flavours of class members like private, protected, etc. A Java interface should be implemented using keyword “implements”; A Java abstract class should be extended using keyword “extends”. An interface can extend another Java interface only, an abstract class can extend another Java class and implement multiple Java interfaces. A Java class can implement multiple interfaces but it can extend only one abstract class.

然而,面试官并不满意,他告诉我这种描述代表了“书本知识”。

他让我给出一个更实际的回答,用实际的例子解释我什么时候会选择抽象类而不是接口。

我哪里错了?


当前回答

为了让你能在面试中给出一个简单、合理的回答,我提供以下几点:

接口用于为一系列相关类指定API——关系就是接口。通常用于具有多个实现的情况,通过配置或在运行时选择正确的实现。(除非使用Spring,此时接口基本上就是Spring Bean)。接口通常用于解决多重继承问题。

抽象类是专门为继承而设计的类。这也意味着有多个实现,所有实现都有一些共性(在抽象类中可以找到)。

如果你想解决这个问题,那么就说抽象类经常实现接口的一部分——job就是你的了!

其他回答

在Java中选择接口是为了避免多重继承中的菱形问题。

如果你想让你所有的方法都由你的客户端实现,你可以选择接口。这意味着你要抽象地设计整个应用程序。

如果您已经知道它们的共同点,那么您就可以选择抽象类。例如,取一个抽象类Car。在更高的级别上实现常见的car方法,如calculateRPM()。这是一种常见的方法,你让客户端实现他自己的行为,比如 calculateMaxSpeed()等。也许你会举几个你在日常工作中遇到的真实例子来解释。

嗯,现在人们渴望实用的方法,你说得很对,但大多数面试官看起来是按照他们目前的要求,想要一个实用的方法。

回答完你的问题后,你应该跳到例子上:

文摘:

例如,我们有一个工资函数,它有一些对所有员工共同的参数。然后我们可以有一个叫做CTC的抽象类,它带有部分定义的方法体,它将被所有类型的员工扩展,并根据他们的额外工资得到补偿。 对于公共功能。

public abstract class CTC {

    public int salary(int hra, int da, int extra)
    {
        int total;
        total = hra+da+extra;
        //incentive for specific performing employee
        //total = hra+da+extra+incentive;
        return total;
    }
}

class Manger extends CTC
{
}


class CEO extends CTC
{
}

class Developer extends CTC
{   
}

接口

Java中的接口允许在不扩展接口功能的情况下拥有接口功能,你必须清楚你想在应用程序中引入的功能签名的实现。它会迫使你做出定义。 不同的功能。

public interface EmployeType {

    public String typeOfEmployee();
}

class ContarctOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "contract";
    }

}

class PermanentOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "permanent";
    }

}

通过将methgos定义为抽象类,你也可以对抽象类进行这样的强制活动,现在一个扩展抽象类的类仍然是抽象类,直到它覆盖抽象函数。

在抽象类中,可以编写方法的默认实现!但是在Interface中你不能。基本上,在接口中存在纯虚方法,这些方法必须由实现接口的类来实现。

1.1抽象类与接口的区别

1.1.1. Abstract classes versus interfaces in Java 8
1.1.2. Conceptual Difference:

1.2 Java 8中的接口默认方法

1.2.1. What is Default Method?
1.2.2. ForEach method compilation error solved using Default Method
1.2.3. Default Method and Multiple Inheritance Ambiguity Problems
1.2.4. Important points about java interface default methods:

1.3 Java接口静态方法

1.3.1. Java Interface Static Method, code example, static method vs default method
1.3.2. Important points about java interface static method:

1.4 Java功能接口



1.1.1. Java 8中的抽象类与接口

Java 8 interface changes include static methods and default methods in interfaces. Prior to Java 8, we could have only method declarations in the interfaces. But from Java 8, we can have default methods and static methods in the interfaces. After introducing Default Method, it seems that interfaces and abstract classes are same. However, they are still different concept in Java 8. Abstract class can define constructor. They are more structured and can have a state associated with them. While in contrast, default method can be implemented only in the terms of invoking other interface methods, with no reference to a particular implementation's state. Hence, both use for different purposes and choosing between two really depends on the scenario context.

1.1.2. 概念上的差别:

抽象类对接口的骨架(即部分)实现有效,但如果没有匹配的接口就不应该存在。

因此,当抽象类被有效地简化为低可见性的接口骨架实现时,默认方法是否也能消除这种情况呢?毫无疑问:不!实现接口几乎总是需要一些或全部的类构建工具,而默认方法不具备这些工具。如果某些接口没有,这显然是一个特例,不应该让您误入歧途。

1.2 Java 8中的接口默认方法

Java 8引入了“默认方法”或(防御方法)新特性,允许开发人员在不破坏现有接口实现的情况下向接口添加新方法。它提供了灵活性,允许接口定义实现,在具体类无法为该方法提供实现的情况下,接口定义实现将作为默认值使用。

让我们看一个小例子来理解它是如何工作的:

public interface OldInterface {
    public void existingMethod();
 
    default public void newDefaultMethod() {
        System.out.println("New default method"
               + " is added in interface");
    }
}

下面的类将在Java JDK 8中成功编译,

public class OldInterfaceImpl implements OldInterface {
    public void existingMethod() {
     // existing implementation is here…
    }
}

如果你创建一个OldInterfaceImpl实例:

OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod(); 

1.2.1. 默认的方法:

默认方法从来不是最终的,不能同步,也不能 重写Object的方法。他们总是公开的,这很严重 限制了编写简短且可重用方法的能力。

缺省方法可以提供给接口而不影响类的实现,因为它包含实现。如果在用实现定义的接口中添加的每个方法都不影响实现类。实现类可以覆盖接口提供的默认实现。

默认方法允许向现有接口添加新功能 而不会破坏这些接口的旧实现。

当我们扩展一个包含默认方法的接口时,我们可以执行以下操作,

不重写默认方法,将继承默认方法。 重写默认方法,类似于重写中的其他方法 子类。 将默认方法重新声明为抽象,从而强制子类为 覆盖它。


1.2.2. 使用默认方法解决了所有方法编译错误

对于Java 8, JDK集合已经被扩展,forEach方法被添加到整个集合中(它与lambdas一起工作)。使用常规方法,代码如下所示,

public interface Iterable<T> {
    public void forEach(Consumer<? super T> consumer);
}

由于此结果每个实现类都有编译错误,因此,默认方法添加了必需的实现,以便现有的实现不应被更改。

具有默认方法的可迭代接口如下所示,

public interface Iterable<T> {
    public default void forEach(Consumer
                   <? super T> consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}

同样的机制已经被用于在不破坏实现类的情况下在JDK接口中添加流。


1.2.3. 默认方法与多重继承歧义问题

由于java类可以实现多个接口,并且每个接口可以定义具有相同方法签名的默认方法,因此,继承的方法之间可能会发生冲突。

考虑下面的例子,

public interface InterfaceA {  
       default void defaultMethod(){  
           System.out.println("Interface A default method");  
    }  
}
 
public interface InterfaceB {
   default void defaultMethod(){
       System.out.println("Interface B default method");
   }
}
 
public class Impl implements InterfaceA, InterfaceB  {
}

上述代码将无法编译,并出现以下错误:

类Impl继承defaultMethod()的不相关默认值 类型为InterfaceA和InterfaceB

为了修复这个类,我们需要提供默认的方法实现:

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
    }
}

此外,如果我们想调用任何一个super Interface提供的默认实现,而不是我们自己的实现,我们可以这样做:

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
        // existing code here..
        InterfaceA.super.defaultMethod();
    }
}

我们可以选择任何默认实现,也可以同时选择两者作为新方法的一部分。

1.2.4. java接口默认方法的要点:

Java interface default methods will help us in extending interfaces without having the fear of breaking implementation classes. Java interface default methods have bridge down the differences between interfaces and abstract classes. Java 8 interface default methods will help us in avoiding utility classes, such as all the Collections class method can be provided in the interfaces itself. Java interface default methods will help us in removing base implementation classes, we can provide default implementation and the implementation classes can chose which one to override. One of the major reason for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions. If any class in the hierarchy has a method with same signature, then default methods become irrelevant. A default method cannot override a method from java.lang.Object. The reasoning is very simple, it’s because Object is the base class for all the java classes. So even if we have Object class methods defined as default methods in interfaces, it will be useless because Object class method will always be used. That’s why to avoid confusion, we can’t have default methods that are overriding Object class methods. Java interface default methods are also referred to as Defender Methods or Virtual extension methods.

资源链接:

何时使用:Java 8+接口默认方法,vs.抽象方法 JDK 8时代的抽象类与接口 通过虚拟扩展方法进行接口演化


1.3 Java接口静态方法

1.3.1. Java接口静态方法,代码示例,静态方法与默认方法

Java接口静态方法与默认方法类似,只是我们不能在实现类中重写它们。这个特性可以帮助我们避免在实现类中实现不好的情况下产生不希望看到的结果。让我们用一个简单的例子来研究这个问题。

public interface MyData {

    default void print(String str) {
        if (!isNull(str))
            System.out.println("MyData Print::" + str);
    }

    static boolean isNull(String str) {
        System.out.println("Interface Null Check");

        return str == null ? true : "".equals(str) ? true : false;
    }
}

现在让我们来看看一个实现类,它有一个isNull()方法,但实现很差。

public class MyDataImpl implements MyData {

    public boolean isNull(String str) {
        System.out.println("Impl Null Check");

        return str == null ? true : false;
    }
    
    public static void main(String args[]){
        MyDataImpl obj = new MyDataImpl();
        obj.print("");
        obj.isNull("abc");
    }
}

注意isNull(String str)是一个简单的类方法,它不会覆盖接口方法。例如,如果我们将@Override注释添加到isNull()方法,它将导致编译器错误。

现在,当我们运行应用程序时,会得到以下输出。

接口空检查 Impl Null检查

如果我们将接口方法从静态改为默认,我们将得到以下输出。

Impl Null检查 MyData打印: Impl Null检查

Java接口静态方法只对接口方法可见,如果我们从MyDataImpl类中删除isNull()方法,我们将不能将它用于MyDataImpl对象。然而,像其他静态方法一样,我们可以使用类名来使用接口静态方法。例如,一个有效的语句将是:

boolean result = MyData.isNull("abc");

1.3.2. java接口静态方法的要点:

Java interface static method is part of interface, we can’t use it for implementation class objects. Java interface static methods are good for providing utility methods, for example null check, collection sorting etc. Java interface static method helps us in providing security by not allowing implementation classes to override them. We can’t define interface static method for Object class methods, we will get compiler error as “This static method cannot hide the instance method from Object”. This is because it’s not allowed in java, since Object is the base class for all the classes and we can’t have one class level static method and another instance method with same signature. We can use java interface static methods to remove utility classes such as Collections and move all of it’s static methods to the corresponding interface, that would be easy to find and use.


1.4 Java功能接口

在我结束这篇文章之前,我想简单介绍一下Functional接口。只有一个抽象方法的接口称为功能接口。

引入了一个新的注释@FunctionalInterface,将接口标记为Functional interface。@FunctionalInterface注释是避免在函数接口中意外添加抽象方法的工具。这是可选的,但使用它是很好的实践。

函数式接口是人们期待已久的Java 8特性,因为它允许我们使用lambda表达式来实例化它们。添加了一个新的包java.util.function,其中包含一系列函数接口,为lambda表达式和方法引用提供目标类型。我们将在以后的文章中讨论函数接口和lambda表达式。

资源位置:

Java 8接口更改-静态方法,默认方法

接口是一个“契约”,其中实现契约的类承诺实现方法。举个例子,当我将一款游戏从2D升级到3D时,我不得不编写一个界面而不是类。我必须创建一个界面来共享2D和3D版本的游戏类别。

package adventure;
import java.awt.*;
public interface Playable {
    public void playSound(String s);
    public Image loadPicture(String s);    
}

然后我可以实现基于环境的方法,同时仍然能够从一个不知道正在加载的游戏版本的对象调用这些方法。

公共类Adventure扩展了JFrame实现了Playable

公共类Dungeon3D扩展了SimpleApplication实现的Playable

公共类Main扩展了SimpleApplication实现了AnimEventListener ActionListener,播放

通常,在游戏世界中,世界可以是一个抽象类,在游戏中执行方法:

public abstract class World...

    public Playable owner;

    public Playable getOwner() {
        return owner;
    }

    public void setOwner(Playable owner) {
        this.owner = owner;
    }