Java study notes --- Interface

We class interface and internal interface to provide a more structured way to achieve the separation;

First of all need to learn abstract class, it is a golden mean between normal classes and interfaces; it is impossible to always use pure interface, abstract class still plays an important role.

  • Abstract classes and abstract methods
  • interface
  • Complete decoupling
  • Multiple inheritance in Java
  • To expand the interface through inheritance
  • Adapter Interface
  • Interface domain
  • Nesting Interface
  • Interface factory

Abstract classes and methods 1. Abstract

In the example in the previous chapter, the base class is often the Instrument "dumb" (the dump) method. Instrument aim is to create a class for all its derived class common interface .

Reason to establish a common interface:

Different subclasses of this interface can be represented in different ways. Through the interface to establish a basic form, as an expression of the common part of all exported classes;

Another argument is the abstract base class called Instrument type, referred to as an abstract class .

If only one of such abstract class like Instrument, objects of this class so little sense, create an abstract class is the hope that through this common interface to manipulate a series of classes.

Therefore, Instrument only represent an interface, no specific implementing content; in fact, Java prohibiting the use of these create an abstract class object. At compile time will catch these questions:

To this end, Java provides a named abstract method mechanism, this approach is incomplete; only a declaration and no method body. The syntax is as follows:

abstract void f();

The method comprising abstract class called abstract class. If a class contains one or more abstract methods, the class must be defined as an abstract class. (Otherwise, the compiler will report an error.)

Since the object is to create abstract class is unsafe, so we'll get an error message from the compiler where the compiler will ensure the purity of the abstract class, do not worry about misuse it.

If you inherit from an abstract class, and want to create the new class of objects, it must provide method definitions for all the abstract methods in the base class. If you do not do this (you can also choose not to), then the derived class will also abstract class, and the compiler will force us to use the abstract keyword to define the class.

We can also create a no abstract class abstract methods. Consider this scenario: If you have a class, let it contain any abstract methods have become moot, and we want to prevent any object of this class, then the time to do so is very useful.

How will the last chapter of the Instrument class transformed into abstract classes (abstract class does not need all the methods are abstract, as long as there is an abstract method is an abstract class) :

Here the Instrument class play () and adjust () method becomes abstract method, so this is an abstract class, must be declared as abstract class and derived class must implement the two abstract methods, or derived class is still abstract class;

We can see, in addition to the base class, almost no change.

Create abstract classes and abstract methods are very useful because they can make the abstract class of clear up, and tells the user how and compilers intend to use them. An abstract class is still very refactoring tools, because they enable us to common method to easily move up the inheritance hierarchy.

to sum up: 

Abstract methods: the abstract foregoing statement, as long as the method thereof, there is no specific implementation

Abstract class: abstract class contains one or more abstract methods, but generally do not include all are abstract, are all abstract methods will next be described the interface; abstract class declared abstract required

An abstract class can be inherited, but can not create object derived class must implement all abstract methods, or still is an abstract class, and must be added abstract statement

2. Interface

the interface keyword method is more abstract concept a step forward. abstract keyword allows one to create one or more no defined methods in the class - provides the interface portion, but does not provide any particular any corresponding implementations that are inherited by the class thus created.

interface keyword produces a completely abstract class, it does not provide any specific implementation. It allows the creator to determine the method name, return type and parameter list, but there is no method body. The interface provides only a form, without providing any specific implementation.

Any code that uses a particular interface knows what methods can be called the interface, but just know that. Thus, the interface is used to establish agreement between the classes and class

要想创建一个接口,需要用interface关键字来替代calss关键字,就像类一样,可以在interface关键字前面添加public关键字(但仅限于该接口在与其同名的文件中被定义)。如果不添加public关键字,则它只具有包访问权限,这样就只能在同一个包内可用。接口也可以包含域,但是这些域隐式的是static和final的

要让一个类遵循某个特定接口(或者是一组接口),需要使用implements关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的”,除此之外,它看起来还很像继承。"乐器"示例的图说明了这一点:

在程序里,无法证明Instrument是一个普通类、抽象类或者还是一个接口。 

3.完全解耦

只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果想要将这个方法应用于不在此继承结构中的某个类,那就会出现问题。接口可以在很大程度上放宽这种限制,因此,它使得我们可以编写可复用性更好的代码。

例如:假设有一个Processor类,他有一个main()方法;另外还有一个process()方法,该方法接受输入参数,修改它的值,然后产生输出。这个类作为基类而被扩展,用来创建各种不同类型的Processor。在本例中,Processor的子类将修改String对象(返回类型可以是协变类型,而非参数类型):

Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。

像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式

这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分策略就是传递进去的参数对象,它包含要执行的代码

在这里,Processor对象就是一个策略,在main()中可以看到有三种不同类型的策略应用到了String类型的s对象上。

string.toUpperCase();针对String类型,将String全部转换成大写;

string.toLowerCase()针对String类型,将String全部转换成小写;

split()方法是String类的一部分,它接受String类型的对象,并以传递进来的参数作为边界,将该String对象分隔开,然后返回一个数组String[]。可以当做创建String数组的快捷方法。

再看一个例子:

现在假设发现了一组电子滤波器,它们看起来好像适用于Apply.process()方法(好像适用是因为这组滤波器的方法名和参数都相同,也就是耦合过紧):

Filter和Processor具有相同的接口元素,但是因为它并非继承自Processor--因为Filter类的创建者压根不清楚你想要将他用作Processor--因此不能将Filter用于Apply.process()方法,即便这样做可以正常运行

这里主要是因为Apply.process()方法和Processor之间的耦合过紧,已经超出了所需要的程度,这就使得应该复用Apply.process()的代码时,复用却被禁止了。

另外还需要注意的是,它们的输入和输出都是Waveform。

但是,如果Processor是一个接口,那么这些限制就会变得松动,使得你可以复用结构该接口的Apply.process()。下面是Processor和Apply的修改版本:

复用代码的第一种方式是客户端程序员遵循该接口来编写它们自己的类,就像下面这样:

但是经常碰到的情况是无法修改你想要使用的类,例如:在电子滤波器中,类库是发现的,并不是自己创建的,在这些情况下,可以使用适配器设计模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口,就像下面这样:

在这种使用适配器的方式中,FilterAdapter的构造器接受你所拥有的接口Filter,然后生成具有你所需要的Processor借口的对象。

在FilterAdapter类中还用到了代理;

将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具有可复用性。 

4.Java中的多重继承

接口不仅仅是一种更纯粹形式的抽象类,接口没有任何具体实现--也就是说,没有任何与接口相关的存储;因此,也就无法组织多个接口的组合。

在C++中,组合多个类的接口的行为被称作多重继承,这会造成很大的压力,因为每个类都有一个具体实现;

在Java中,可以执行相同的行为,但是只有一个类可以有具体实现;因此,通过组合多个接口,C++中的问题是不会在Java中发生的。

在导出类中,不强制要求必须有一个是抽象的或“具体的”基类。如果要从一个非接口的类继承,那么只能从一个类去继承,其余的基元素必须是接口。

需要将所有的接口名都置于implements关键字之后,用逗号将它们一一隔开。

可以继承任意多个接口,并可以向上转型为每个接口,因为每个接口都是一个独立类型。

下面例子展示了一个具体类组合数个接口之后产生一个新类:

 

Hero组合了具体类ActionCharacter和接口CanFight、CanSwim和CanFly。当通过这种方式讲一个具体类和多个接口组合到一起时,这个具体类必须放在前面,后面跟着的才是接口。

这里注意到,CanFight接口与ActionCharacter类中的fight()方法的特征签名是一样的,而且,在Hero中并没有提供fight()的定义。

可以扩展接口,但是得到的只是另一个接口。

当想要创建对象时,所有的定义首先必须都存在,即使Hero没有显式地提供fight()的定义,其定义也因ActionCharacter而随之而来,这样就使得创建Hero对象成为了可能。

在Adventure类中,可以看到有四个方法把上述各种接口和具体类作为参数,当Hero对象被创建时,它可以被传递给这些方法中的任何一个,这意味着它一次被向上转型为每一个接口。Java的这种设计接口的方式,使得这项工作并不需要程序员付出任何特别的努力。

使用接口的核心原因

1).为了能够向上转型为多个基类型(以及由此带来的灵活性);

2).防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口(这与使用抽象类原因相同)

这带来的一个问题是,应该使用接口还是抽象类?

如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。事实上,若知道某事物应该成为一个基类,那么第一选择应该是接口。 

5.通过继承来扩展接口

通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口,这两种方式都可以获得新的接口。

 

DangerousMonster是Monster的直接扩展,它产生了一个新接口。DragonZilla中实现了这个接口; 

在Vampire中使用的语法仅适用于接口继承,一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口,只需用逗号将接口名一一分隔开即可。

组合接口时的名字冲突

在实现多重继承时,会碰到一个小陷阱,在前面的例子中,CanFight和ActionCharacter都有一个相同的void fight()方法。问题不是它们方法相同,问题是,如果它们的签名(参数)返回类型不同,会怎么样呢?

此时困难来了,因为覆盖、实现和重载令人不快的搅在一起,而且重载方法仅通过返回类型是区分不开的。当撤销最后两行的注释时,下列错误消息说明了这一切:

 

在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱。 

6.适配接口

接口最吸引人的原因之一就是允许同一个接口具有多个不同的具体实现。在简单的情况中,它的体现形式通常是一个接受接口类型的方法,而该接口的实现和向该方法传递的对象则取决于方法的使用者

因此,接口的一种常见用法就是前面提到的策略设计模式。此时你编写一个执行某些操作的方法,而该方法接受一个同样是你指定的接口。你主要就是要声明:“你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口”。这使得方法更加灵活、通用,并更具有可复用性。

例如,Java SE5的Scanner类的构造器接受的就是一个Readable接口,Readable没有用作Java标准类库中其他任何方法的参数,它是单独为Scanner创建的,以使得Scanner不必将其参数限制为某个特定类。通过这种方式,Scanner可以作用于更多的类型。如果你创建了一个新的类,并且想让Scanner可以作用于它,那么你就应该让它称为Readable,就像下面这样:

也就是说,在Scanner构造器内部,接受一个参数,这个参数是Readable的一个对象;

Readable接口可以实现read()方法,在read()内部,将输入内容添加到CharBuffer参数中,或者在没有任何输入时返回-1;

假设你还有一个还未实现Readable的类,怎样才能让Scanner作用于它呢?下面这个类就是一个例子,它可以产生随机浮点数:

还未实现Readable是指,没有实现read()方法?

再次使用了适配器模式,在这里,被适配的类可以通过继承和实现Readable接口来创建。因此,通过使用interface关键字提供的伪多重继承机制,可以生成的既是RandomDoubles又是Readable的新类:

在这种方式中,可以在任何现有类之上添加新的接口,所以这意味着让方法接受接口类型,是一种让任何类都可以对该方法进行适配的方式。这就是使用接口而不是类的强大之处。 

7.接口中的域

因为放入接口中的任何域都是static和final的,所以接口就成为了一种很便捷的用来创建常量组的工具。在Java SE5之前,这是产生与C++中的enum(枚举类型)具有相同效果的类型的唯一途径。因此,在Java SE5之前的代码会看到下面这样的代码:

Java中标识具有常量初始值的static final时,会使用大写字母的风格(在一个标识符中用下划线来分隔多个单词);接口中的域自动是public的,所以没有显式地指明这一点。

有了Java SE5,既可以使用更加强大而灵活的enum关键字,因此,使用接口来群组常量已经显得没什么意义了。

初始化接口中的域: 

在接口定义的域不能是“空final”,但是可以被非常量表达式初始化。例如:

既然,域是static的,它们就可以在类第一次被加载时初始化,这发生在任何域首次访问时。下面是一个测试:

 

当然,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。 

8.嵌套接口

接口可以嵌套在类或其他接口中。这揭示了许多有趣的特性:

在类中嵌套接口的语法是相当显而易见的,就像非嵌套接口一样,可以拥有public和“包访问”两种可视性。

作为一种新添加的方式,接口也可以被实现为private的,就像在A.D中所看到的(相同的语法既适用于嵌套接口,也适用与嵌套类)。那么private的嵌套接口能带来什么好处呢?

可能会猜想,它只能够被实现为DImp中的一个private内部类,但是A.DImp2展示了它同样可以被实现为public类。但是,A.DImp2只能被其自身所使用。

你无法说他实现了一个private接口D。因此,实现一个private接口只是一种方式,他可以强制该接口中的方法定义不要添加任何类型信息(也就是说,不允许向上转型)。

getD()方法使我们陷入一个进退两难的境地,这个问题与private接口相关:它是一个返回对private接口的引用的public方法。你对这个方法的返回值能做什么呢?在main()中,可以看到数次尝试使用返回值的行为都失败了。只有一种方式可成功,那就是将返回值交给有权使用它的对象(在这里只有D和A有权使用DImp2类,但前面不是说不允许向上转型吗?)。在本例中,是另一个A通过receiveD()方法来实现。

接口E说明接口接口彼此之间也可以嵌套,然而,作用域接口的各种规则,特别是所有的接口元素都必须是public的,在此都会被严格执行。因此,嵌套在另一个接口中的接口自动就是public的,而不能被声明为private的。

NestingInterface展示了嵌套接口的各种实现方式。特别要注意的是,当实现某个接口时,并不需要实现嵌套在其内部的任何接口。而且private接口不能在定义它的类之外被实现。

添加这些特性的最初原因可能是处于对严格的语法一致性的考虑,但是作者认为,一旦了解了某种特性,就总能够找到他的用武之地。

9.接口与工厂

接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式

这与直接调用构造器不同,在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象

理论上,通过这种方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。下面的实例展示了工厂方法的结构:

 如果不是用工厂方法,你的代码就必须在某处指定将要创建的Service的确切类型,以便调用合适的构造器。

为什么想要创建这种额外级别的间接性?一个常见的原因就是想要创建框架:假设你正在创建一个对弈游戏系统,例如,在相同的棋盘上下国际象棋和西洋跳棋:

如果Games类表示一段复杂的代码,那么这种方式就允许你在不同类型的游戏中复用这段代码。你可以想象一些能够从这个模式中受益的更加精巧的游戏。

下一章的工厂实现方式是:内部类。

 

 

 

 

Guess you like

Origin www.cnblogs.com/ifreewolf/p/11254012.html
Recommended