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類型的值,而這個值可能會被給賦上其他的值,所以是不安全的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章