Interfaces & Inner Classes

Interfaces and inner classes provide more sophisticated ways to organize and control the objects in your system.

C++, for example, does not contain such mechanisms, although the clever programmer may simulate them. The fact that they exist in Java indicates that they were considered important enough to provide direct support through language keywords.

接口和内部类提供了更加完善的方式来在系统中组织和控制对象。举例来说,C++并没有提供这种机制,但是一些聪明的程序员可以通过伪装来实现。在Java存在的主要理由是认为语言的关键词应该直接提供直接的支持。

In Chapter 7 you learned about the abstract keyword, which allows you to create one or more methods in a class that have no definitions—you provide part of the interface without providing a corresponding implementation, which is created by inheritors. The interface keyword produces a completely abstract class, one that provides no implementation at all. You’ll learn that the interface is more than just an abstract class taken to the extreme, since it allows you to perform a variation on C++’s “multiple inheritance” by creating a class that can be upcast to more than one base type.

在第七章中我们学习到了abstract关键词,允许你在类中创建一个或者多个没有定义的方法-提供了接口的一部分但是没有实现,这部分由派生类实现。而interface关键字则可以会创建出一个真正意义的abstract类,所有的方法都没有实现。你可以看到interface其实是abstract的极端表现,它能够像在C++中一样“多重继承”来创建类,这样进行“上传转化”的时候就不仅仅是一个基类了。

At first, inner classes look like a simple code-hiding mechanism: you place classes inside other classes. You’ll learn, however, that the inner class does more than that—it knows about and can communicate with the surrounding class—and that the kind of code you can write with inner classes is more elegant and clear, although it is a new concept to most. It takes some time to become comfortable with design using inner classes.

首先,内部类看起来像是一种简单的代码隐藏机制:你将一个类放置到另外一个类当中去。然而,你将会知道内部类不仅仅是提到的这些功能,它还可以与surrounding class进行通讯,并且你会感觉在内部类写代码很舒服也很清晰,尽管这个概念对于很多人来说是一个新概念,经历一段时间之后你会觉得使用内部类设计很舒服。

Interfaces

The interface keyword takes the abstract concept one step further. You could think of it as a “pure” abstract class. It allows the creator to establish the form for a class: method names, argument lists, and return types, but no method bodies. An interface can also contain fields, but these are implicitly static and final. An interface provides only a form, but no implementation.

Interface关键词将abstract的概念推到了更高的层次。你可以将它理解为纯粹的抽象类。它允许创建者定义类的形式:方法名字,参数列表,返回值类型,但是没有人和实现。接口也可以包含数据项,但是它只能是static或者final。接口只是提供了形式但是没有任何实现。

An interface says, “This is what all classes that implement this particular interface will look like.” Thus, any code that uses a particular interface knows what methods might be called for that interface, and that’s all. So the interface is used to establish a “protocol” between classes. (Some object-oriented programming languages have a keyword called protocol to do the same thing.)

接口即为“规定了继承它的类的形式”。从而,所有使用interface的代码都知道接口有哪些方法提供调用,仅此而已。因此,iterface是用来建立在类之间的协议。(在一些面向对象开发语言中确有一个protocol关键词来实现这样的事情)。

To create an interface, use the interface keyword instead of the class keyword. Like a class, you can add the public keyword before the interface keyword (but only if that interface is defined in a file of the same name) or leave it off to give package access, so that it is only usable within the same package.

通过使用interface关键词代替class关键词来创建接口类。作为一个类你可以在Interface关键词之前增加public关键词(只有保存的文件名字和interface相同的才可以)。或者不写留给接口类package级别的访问权限,这样的话就只能在这个包里使用了。

To make a class that conforms to a particular interface (or group of interfaces), use the implements keyword, which says, “The interface is what it looks like, but now I’m going to say how it works.” Other than that, it looks like inheritance. The diagram for the instrument example shows this:

要创建一个实现了一个接口或者多个接口的类,需要使用implements关键词,也就是说“接口说明了需要作什么,但是现在就是来说如何实现了”。除此之外,它和继承没有什么区别,下面关于乐器的图标给我们展示了这些:

You can see from the Woodwind and Brass classes that once you’ve implemented an interface, that implementation becomes an ordinary class that can be extended in the regular way.

You can choose to explicitly declare the method declarations in an interface as public, but they are public even if you don’t say it. So when you implement an interface, the methods from the interface must be defined as public. Otherwise, they would default to package access, and you’d be reducing the accessibility of a method during inheritance, which is not allowed by the Java compiler.

你可以通过从WoodwindBrass类看出来:一旦实现了某个接口,这个实现类就变成了一个你可以直接继承的类。你可以在接口类中很清晰的声明一个方法是public的,但是即使你不写明,这些方法的访问权限也是Public的,所以当你实现一个接口的时候,你必须把这些继承下来的方法声明为public,否则这些方法将默认为package级别访问权限,这样在继承类中就降低了这些方法的访问权限,Java的编译器是不允许这样的。

You can see this in the modified version of the Instrument example. Note that every method in the interface is strictly a declaration, which is the only thing the compiler allows. In addition, none of the methods in Instrument are declared as public, but they’re automatically public anyway:

你在修改过的Instrument例子中看到,编译器只允许在Interface中方法都仅仅是声明。另外,Instrument类中没有一个方法声明为了public,但是它们默认为public

The rest of the code works the same. It doesn’t matter if you are upcasting to a “regular” class called Instrument, an abstract class called Instrument, or to an interface called Instrument. The behavior is the same. In fact, you can see in the tune( ) method that there isn’t any evidence about whether Instrument is a “regular” class, an abstract class, or an interface. This is the intent: Each approach gives the programmer different control over the way objects are created and used.

剩余部分的代码还是一样的运转执行,不管将对象上传转化为了普通的Instrument类或者Instrument的抽象类都无不影响程序正常运行。实际上,我们可以通过tune()方法找不到任何证据表明Instrument到底是一个普通类,抽象类还是接口。事实上,它就是提供给了程序员多中方法去创建和控制以及使用这些对象。

“Multiple inheritance” in Java

The interface isn’t simply a “more pure” form of abstract class. It has a higher purpose than that. Because an interface has no implementation at all—that is, there is no storage associated with an interface—there’s nothing to prevent many interfaces from being combined. This is valuable because there are times when you need to say “An x is an a and a b and a c.” In C++, this act of combining multiple class interfaces is called multiple inheritance, and it carries some rather sticky baggage because each class can have an implementation. In Java, you can perform the same act, but only one of the classes can have an implementation, so the problems seen in C++ do not occur with Java when combining multiple interfaces:

接口当然不仅仅是一个纯粹的抽象类,它有更高的用途。因为在接口中没有任何的实现部分,在接口中没有涉及到存储,不会有什么东西去阻止接口进行组合,这是很重要的,因为我们有时候也会需要“X既是A又是B又是C”。在C++语言中,将多个类的接口声明到一起的行为叫做“多重继承”。由于每个类都有自己的实现,所以这随之也给C++带来了比较大的麻烦。在Java中你可以实现同样的行为,但是仅仅只有一个类可以有实现,所以在C++中出现的问题在Java中就不会出现了。

In a derived class, you aren’t forced to have a base class that is either an abstract or “concrete” (one with no abstract methods). If you do inherit from a non-interface, you can inherit from only one. All the rest of the base elements must be interfaces. You place all the interface names after the implements keyword and separate them with commas. You can have as many interfaces as you want; each one becomes an independent type that you can upcast to. The following example shows a concrete class combined with several interfaces to produce a new class:

在派生类中,并不是必须要继承一个抽象类或者一个具体的类(没有抽象方法的类)。如果你继承的不是一个接口类,那么你只能继承一个类。剩余部分的基本元素就必须是接口了。把所有的接口类的名字放到Implents关键词的后面并且使用逗号隔开,对于接口类数量没有限制;你可以将对象上传转化为其中人和一个独立的类型。下面的例子中conrrete类通过继承多个接口从而创建的:

You can see that Hero combines the concrete class ActionCharacter with the interfaces CanFight, CanSwim, and CanFly. When you combine a concrete class with interfaces this way, the concrete class must come first, then the interfaces. (The compiler gives an error otherwise.)

可以看到Hero类组合继承了具体的类ActionCharacter接口CanFigth,CanSwim,以及CanFly。像这种情况继承具体的类以及接口的方式,那么具体的类必须在接口的前面,否则的话编译器会提示错误信息。

Note that the signature for fight( ) is the same in the interface CanFight and the class ActionCharacter, and that fight( ) is not provided with a definition in Hero. The rule for an interface is that you can inherit from it (as you will see shortly), but then you’ve got another interface. If you want to create an object of the new type, it must be a class with all definitions provided. Even though Hero does not explicitly provide a definition for fight( ), the definition comes along with ActionCharacter, so it is automatically provided and it’s possible to create objects of Hero.

可以看到ActionCharacter类中的fight()方法和interface中的CanFight()的特征非常相似,但是在Hero中并没有对fight()进行定义,关于接口的规则是你可以继承,但是你继承下来的还是接口。如果你想创建一个新的类型那么它继承下来所有的接口。虽然Hero没有明确的提供一个fight()的方法,但是Abstracter提供了,所以Hero类自然也就有了,从而就可以创建Hero的对象了。

In class Adventure, you can see that there are four methods that take as arguments the various interfaces and the concrete class. When a Hero object is created, it can be passed to any of these methods, which means it is being upcast to each interface in turn. Because of the way interfaces are designed in Java, this works without any particular effort on the part of the programmer.

Adventure类中有四个接口类以及具体类来作为方法的参数。当创建一个Hero的对象的时候,就会调用这些方法,这意味着那些对象要被上传转化为接口类的对象。因为这种接口的设计是属于Java本身设计的,所以不需要程序员写人和额外的代码来处理。

Keep in mind that the core reason for interfaces is shown in the preceding example: to be able to upcast to more than one base type. However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface. This brings up a question: Should you use an interface or an abstract class? An interface gives you the benefits of an abstract class and the benefits of an interface, so if it’s possible to create your base class without any method definitions or member variables, you should always prefer interfaces to abstract classes. In fact, if you know something is going to be a base class, your first choice should be to make it an interface, and only if you’re forced to have method definitions or member variables should you change to an abstract class, or if necessary a concrete class.

我们应当记住关于Interface的最大优势已经在前面的代码中展示过了:可以上传转化为多种基类的对象。然而,引入接口的第二个理由是和引入抽象类的原因一样的:避免客户端程序员来建立这些类的对象,告诫他们这只是一个接口。这样就引出了另一个问题:我们是什么时候使用接口什么时候使用抽象类呢?接口既给你提供了抽象类的优势有提供了本身所具有的优势,所以如果你创建自己的基类不包含方法的定义以及成员变量的时候,你应该会更衷情于接口。事实上如果你知道它会编程基类的话,你应该首选把它作为一个接口,除非你必须要提供方法的定义或者成员变量的时候你可以考虑抽象类,或者你需要创建一个具体的类。

Name collisions when combining interfaces

You can encounter a small pitfall when implementing multiple interfaces. In the preceding example, both CanFight and ActionCharacter have an identical void fight( ) method. This is not a problem, because the method is identical in both cases. But what if it isn’t? Here’s an example:

当你实现多个接口的时候你会遇到一些小的麻烦,在上面的例子当中,CanFlightActionCharacter都有一个fight()方法。这不是问题,因为两个方法是同一个方法。但是如果这不是问题的话,又会是什么呢?

The difficulty occurs because overriding, implementation, and overloading get unpleasantly mixed together, and overloaded methods cannot differ only by return type. When the last two lines are uncommented, the error messages say it all:

InterfaceCollision.java:23: f( ) in C cannot implement f( ) in I1; attempting to use incompatible return type
found : int
required: void
InterfaceCollision.java:24: interfaces I3 and I1 are incompatible; both define f( ), but with different return type

这个问题的难点是因为覆写,实现和重载的不期而遇,这些令人头疼的问题交织到了一起,而且仅仅通过返回值类型不同又不能称之为“重载“。如果最后两行不被注释掉的话,就会看到下面的错误提示信息,

Using the same method names in different interfaces that are intended to be combined generally causes confusion in the readability of the code, as well. Strive to avoid it.

在要组合在一起的两个接口中分别建立相同名称的方法本身就会降低程序的可读性,所以应当尽量去避免这么做。

Extending an interface with inheritance

You can easily add new method declarations to an interface by using inheritance, and you can also combine several interfaces into a new interface with inheritance. In both cases you get a new interface, as seen in this example:

DangerousMonster is a simple extension to Monster that produces a new interface. This is implemented in DragonZilla.

The syntax used in Vampire works only when inheriting interfaces. Normally, you can use extends with only a single class, but since an interface can be made from multiple other interfaces, extends can refer to multiple base interfaces when building a new interface. As you can see, the interface names are simply separated with commas.

DangerousMonster是对Monster接口的简单扩展而产生的一个新接口,而这个接口在DragonZilla中得到了实现。在Vampise中用到的语法只有在继承接口的时候才可以使用,平时继承时extends后面只允许跟一个class,但是当一个接口由其他的多个接口组成的时候extends后可以跟随多个接口类从而来创建一个新的interface。当然你也应该看到了多个接口之间通过逗号隔开。

Grouping constants

Because any fields you put into an interface are automatically static and final, the interface is a convenient tool for creating groups of constant values, much as you would with an enum in C or C++. For example:

因为数据项在接口中自动就默认为了staticfinal类型,自然的接口就成为了很方便创建常量之地,就像在C++中的enum一样,例如:

Notice the Java style of using all uppercase letters (with underscores to separate multiple words in a single identifier) for static finals that have constant initializers.

需要注意的是Java的编程风格是所有字母都是大写(常量名称是多个单词的单词之间使用下划线隔开),并且保证采用常量进行初始化。

The fields in an interface are automatically public, so it’s unnecessary to specify that.

在接口中的数据项默认为public级别访问权限,所以没有必要特别的声明为public

You can use the constants from outside the package by importing c08.* or c08.Months just as you would with any other package, and referencing the values with expressions like Months.JANUARY. Of course, what you get is just an int, so there isn’t the extra type safety that C++’s enum has, but this (commonly used) technique is certainly an improvement over hard coding numbers into your programs. (That approach is often referred to as using “magic numbers,” and it produces very difficult-to-maintain code.)

你可以像使用其他的包那样来引入interface所在的包从而来使用这些常量,并且调用这些变量的时候采用这样的表达式“Months.JUANUARY“。当然你得到的数据类型是int型。这里没有像在C++语言中enum一样提供一种特别类型安全,但是这种常用的技术比那种直接在程序里写很多的数字方式有了很大的提高。(这种手法通常被称为”神奇数字“,并且遗留下了很难维护的代码)。

If you do want extra type safety, you can build a class like this:

如果你需要额外的类型安全,那么你可以建立一个像下面这样的类:

Month is a final class with a private constructor, so no one can inherit from it or make any instances of it. The only instances are the final static ones created in the class itself: JAN, FEB, MAR, etc. These objects are also used in the array month, which lets you iterate through an array of Month2 objects. The number( ) method allows you to select a Month by giving its corresponding month number. In main( ) you can see the type safety; m is a Month object so it can be assigned only to a Month. The previous example Months.java provided only int values, so an int variable intended to represent a month could actually be given any integer value, which wasn’t very safe.

Month类是一个具有private构造方法的类,所以任何类都不可以继承Month类,并且也不允许创建Month类的对象,所有的对象都是类本身自己创建的,如:JAN,FEB,MAR,等等。这些对象也被用在了month的数组中,给你提供了一个遍历Month的对象数组,而number()方法则允许你传入一个月份数字来选择对应的月份。在主方法中你可以看到类型安全m是一个Month类的对象,所以只能赋值给Month的对象,而在上面的例子中提供的是int类型的值,而这个值可能会被给赋上其他的值,所以是不安全的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章