Java - 从Interface类型而不是Class声明

jex*_*345 38 java interface

在我正确掌握接口最佳实践的过程中,我注意到了以下声明:

List<String> myList = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)

代替

ArrayList<String> myList = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)

- 要我的理解,是因为它可以让有一天你不希望实现一个ArrayList但也许另一种类型列表的情况下的灵活性.

有了这个逻辑,我建立了一个例子:

public class InterfaceTest {

    public static void main(String[] args) {

        PetInterface p = new Cat();
        p.talk();

    }

}

interface PetInterface {                

    public void talk();

}

class Dog implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Bark!");
    }

}

class Cat implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Meow!");
    }

    public void batheSelf() {
        System.out.println("Cat bathing");
    }

}
Run Code Online (Sandbox Code Playgroud)

我的问题是,我无法访问batheSelf()方法,因为它仅适用于Cat.这使我相信,我应该只从接口中声明,如果我只打算使用的接口(而不是从子类中额外的方法)声明的方法,否则我应该直接从类(在这种情况下,猫)申报.我在这个假设中是否正确?

pol*_*nts 33

当通过它们interface或a 引用对象之间存在选择时class,前者应该是首选的,但仅在存在适当的类型时才是优选的.

考虑作为一个例子.你不应该只是一味地用在preferrence到所有情况,因为这会剥夺你喜欢简单的操作,等等.StringimplementsCharSequenceCharSequenceStringtrim()toUpperCase()

但是,应该使用String仅关注其char值序列的方法,因为在这种情况下,这是适当的类型.这其实与本案的类.CharSequencereplace(CharSequence target, CharSequence replacement)String

另一个例子是java.util.regex.Pattern它的Matcher matcher(CharSequence)方法.这样就不仅Matcher可以创建,也可以创建其他所有内容.PatternStringCharSequence

在库中一个interface应该使用的地方的一个很好的例子,但不幸的是,它也可以在Matcher:它appendReplacementappendTail方法只接受StringBuffer.StringBuilder自1.5以来,这个类已经被它的表兄弟所取代.

A StringBuilder不是a StringBuffer,所以我们不能使用前者的append…方法Matcher.但是,他们俩implementsAppendable(也在1.5中介绍).理想Matcherappend…方法应该接受任何方法Appendable,然后我们就可以使用StringBuilder,以及所有其他Appendable可用的方法!

因此,我们可以看到当适当的类型存在时,如何通过它们的接口引用对象可以是一个强大的抽象,但只有存在这些类型时.如果类型不存在,那么如果有意义,您可以考虑定义自己的类型.Cat例如,在此示例中,您可以定义interface SelfBathable.然后Cat你可以接受任何SelfBathable对象(例如a Parakeet)而不是引用a

如果创建一个新类型没有意义,那么无论如何你都可以通过它来引用它class.

也可以看看

  • Effective Java 2nd Edition,Item 52:通过接口引用对象

    如果存在适当的接口类型,则应使用接口类型声明参数,返回值和字段.如果您养成使用界面类型的习惯,您的程序将更加灵活.如果不存在适当的接口,则通过类引用对象是完全合适的.

相关链接


Bor*_*lid 11

是的,你是对的.您应该声明为提供您使用的方法的最常规类型.

这是多态的概念.


Pau*_*des 5

您是正确的,但如果需要,您可以从界面投射到所需的宠物。例如:

PetInterface p = new Cat();
((Cat)p).batheSelf();
Run Code Online (Sandbox Code Playgroud)

当然,如果您尝试将宠物投射到狗身上,则无法调用 BatheSelf() 方法。它甚至无法编译。因此,为了避免出现问题,您可以采用如下方法:

public void bathe(PetInterface p){
    if (p instanceof Cat) {
        Cat c = (Cat) p;
        c.batheSelf();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用时instanceof,您确保不会尝试让狗在运行时自己洗澡。这会引发错误。