Thinking in Java 8

zhaozj2021-02-11  203

Chapter 7 Polymourne

"For object-oriented programming languages, multiplexity is the third most basic feature (the first two are data abstraction and inheritance."

"Polymorphism) separates the interface from a specific implementation detail from another angle, which is the separation of" what "and" how to do "two modules. Using versatile concepts, the organization and readability of the code can be improved. In addition, you can create "easy to extend" programs. Whether in the creation of the project, they can be easily "growth" when the new feature is needed.

By combining various features and behaviors, packaging techniques create new data types. Through the hidden of specific implementation details, the interface can be separated from the implementation details, making all details "private". This organization's way makes those who have programmed programming backgrounds feel comfortable. But versatility involves the decomposition of "type". Through the study of the previous chapter, everyone knows that by inheriting can treat an object as its own type or its own basic type. This capability is important because multiple types (derived from the same basic type) can be treated as the same type. And only one code is required, you can do the same processing. With a plimming method call, one type can be separated from another similar type, as long as they are derived from the same base type. This distinguishing is achieved by various methods in behavior, which can be called through the basic class.

In this chapter, everyone should learn about the problem of vectors from shallow to deeply (also called dynamic binding, postpone binding or running binding). At the same time, some simple examples are given, all of which are all stripped, only the code related to the versatility is retained.

Top 7.1

In Chapter 6, everyone knows that an object can be used as its own type, or as an object of its basic type. A object handle is obtained, and its behavior used as a basic type handle is called "traceable" - because the painting of the inherited tree is the top right.

But this will also encounter a problem, as shown in the following example (if you have trouble, please refer to Chapter 3 3.1.2 "Assignment":

252-253 page program

Among them, Music.Tune () receives an Instrument handle while also receiving everything from Instrument. This happens when a Wind handle is passed to Tune (). There is no need to shape it. This is acceptable; the interface in Instrument must exist in Wind because Wind is inherited from Instrument. From Wind to Instrument, you may "reduce" that interface, but it is impossible to make it smaller than the full interface of Instrument.

7.1.1 Why is you going back?

This program seems to be a bit strange. Why should everyone have interested in forget the type of object? When you travel back, you may have doubts in this regard. And if tune () simply obtains a Wind handle, use it as its own own variable, it seems to be more simple and intuitive. But pay attention to: If you do it, you need to write a new Tune () for each type of Instrument in the system. Suppose it is in the previous inference, add StrUns and BRASS (Copper Tubes):

253-254 page program

This is to do it, but there is a great drawback: you must prepare a closely related method for each new Instrument2 class. This means that much more programmed amounts are required for the first time. In the future, if you want to add a new method like Tune () or add a new type to INSTRUMENT, you still need a lot of encoding. In addition, even if you forget to overload your own method, the compiler will not prompt any errors. In this way, the entire operation process of the type is extremely difficult to manage, and there is a risk of out-of control. However, if only one method is written, use the basic class as an argument or parameter, rather than using those specific derivatives, isn't it simple? That is, if we can derive the class, only let your code to deal with the basic class, then the amount of work will be difficult to estimate.

This is the place where "versatile" is characterized. However, most programmers (especially for programming background) are still somewhat raw.

7.2 In-depth understanding

For Music.java's difficulties, it can be experienced by running procedures. The output is Wind.Play (). This is of course the output of our hopes, but it seems that it does not want to act according to our hopes. Please observe the tune () method:

Public Static Void Tune (Instrument I) {

// ...

I.Play (Note.Middlec);

}

It receives the INSTRUMENT handle. So in this case, how can the compiler know that the INSTRUMENT handle points to a Wind, not a BRASS or STRINGED? The compiler is not known. In order to understand this problem, we need to explore the theme of "binding".

7.2.1 Binding of the method call

Calling a method calls the same method body to connect together is called "binding". If the binding is performed before the program runs (by the compiler and linker, if any), it is called "early binding". Everyone I have never heard of this term because it is impossible in any programming language. The C compiler has only one way to call, that is, "early binding".

The most confusing place in the above program is related to early binding, because in the premise of only one Instrument handle, the compiler does not know which method of the specific call.

The method of solving is "post-binding", which means that the binding is performed during operation, based on the type of object. The post-binding is also called "dynamic binding" or "running binding". If a language implements a later binding, some mechanisms must be provided, and the type of object can be determined during operation, and the appropriate method is called separately. That is, the compiler still does not know the type of object, but the method call mechanism can go to investigate, find the correct method body. Different languages ​​have different methods of implementation of later bindings. But we can at least think that they must set some special types of information in the object.

All methods binding in Java use post-binding techniques unless a method has been declared into Final. This means that we usually do not have to decide whether it should be binding - it is automatically occurred.

Why do you declare a way to Final? As the previous chapter pointed out, it prevents others from overwriting that method. But maybe more importantly, it can effectively "close" dynamic binding, or tell the compiler that does not need to perform dynamic binding. In this way, the compiler can call the FINAL method to generate higher code.

7.2.2 Creating the right behavior

All methods of knowing Java bindings are backed by later binding, you can write your own code accordingly, communicate with the base class. At this point, all derivative classes ensure that it can work properly with the same code. Or for another method, we can "send a message to an object, let the object to determine what to do." In the object-oriented program, there is a classic "shape" example. Because it is easy to manifest by visualization, it is often used to explain the problem. But unfortunately, it may mislead beginners think that OOP is just designed for graphical programming, this understanding is of course wrong.

The shape example has a basic class, named Shape; there is a large number of derivative types: Circle, Square (square), Triangle (triangle), etc. Everyone likes this example, because it is easy to understand the concept of "a type of shape". The following inheritors show us their relationships:

257 pages

Trace shape can be simply manifested by using this statement:

Shape s = new circle ();

Here, we created a Circle object and assigned the result handle to a shape. This surface seems to be an error operation (assigning a type to another), but it is actually feasible - because in inheritance relationship, Circle belongs to the Shape. Therefore, the compiler recognizes the above statement and will not prompt us to prompt an error message.

When we call one of the basic types (overridden in the derived class):

s.draw ();

Similarly, everyone may think that the Shape's Draw () is called because it is a shape handle after all. So how can the compiler know what else should be other? But at this time, Circle.draw () is Circle.Draw () because the later binding has been intervened (versatile).

The following example explains the problem from a slightly different angle:

257-258 page program

For all things derived from Shape, Shape has built a general interface - that is, all (geometric) shapes can be depicted and deleted. Derivatives covers these definitions, providing a unique behavior for each special type of geometry.

In the primary class shapes, a Static method is included in Randshape (). Its role is to generate a handle for a random-selected Shape object at a timely call. Please note that the shape is in every returnite statement. This statement acquires a handle to a Circle, Square, or Triangle, and transmits it as a return type shape. So whenever you call this method, you will never have a chance to understand what it is, because it will definitely get a simple Shape handle.

Main () contains an array of Shape handles, where data is filled in the call of Randshape (). At this time, we know that you have Shape, but you don't know any specific situation except for this (the compiler also doesn't know). However, when we step in this array and call DRAW () for each element, the correct behavior of each type will happen, just like the output sample below:

259-page program

Of course, since the geometry is randomly selected, each run may have different results. The random choice to highlight the shape is to let everyone know this: In order to make a correct call when compiling, the compiler does not need to obtain any special intelligence. All calls to DRAW () are made by dynamic binding. 7.2.3 scalability

Now let's still return an example of an instrument (inStrument). Due to the presence, any new type can be added to the system according to your own needs, while do not need to change the true () method. In a well-designed OOP program, most of our or all methods will follow the model of Tune () and only communicate with the base interface. We said that such a program has "scalability" because the new data type can be inherited from the general basic class to add some functions. If it is to adapt to the requirements of the new class, the method for manipulating the basic interface does not need to change,

For the instrument example, suppose we add more methods in the basic class, as well as a series of new categories, what happens? The following is a schematic:

260 pages

All of these new classes work with the old-Tune (), do not require any adjustments to tune (). Even if tune () is located in a separate file, Tune () can also work correctly and do not need to recompile if the new method is added to the INSTRUMENT. Below this program is the specific implementation of the above schematic:

Page 260-262

The new method is What () and adjounce (). The former returns a String handle while returning the instructions for that class; the latter makes us adjust each instrument.

In Main (), when we put something in an Instrument3 array, it will automatically trace the inStrument3.

It can be seen that while all of all of the other code surrounding the Tune () method changes, the Tune () method is not affected by their influence, and it is still working properly. This is the goal that uses versatility. After modification of the code, we will not affect the parties that should not be affected in the program. In addition, we believe that it is a crucial technology that allows programmers to "change the change in things that have not changed."

7.3 Covering and Overload

Now let's take a look at the first example of this chapter in different eyes. In the following program, the interface of the method play () changes during the covered process. This means that we do not actually have a "overlay" method, but "overload". The compiler allows us to overload the method so that it does not report an error. But this behavior may not be what we hope. Here is this example:

Page 263

Here is another concept of another easy confusion. In InstrumentX, the Play () method uses an int (integer) value, its identifier is NOTEX. That is, even if Notex is a class name, you can use it as an identifier, the compiler will not report an error. But in WindX, play () uses a NOTEX handle, which has an identifier n. Even if we use "PLAYX NOTEX", the compiler will not report an error. In this way, it looks like a programmer intends to override the play () function, but the type definition of the method is slightly indispensable. However, the compiler is assumed at this time that the programmer is intended to "overload" instead of "override". Please carefully experience the difference between these two terms. "Overload" means a variety of meanings in different places; and "coverage" means that it is only one meaning anytime, anywhere, but the original meaning is completely replaced by later meaning. Please note that if you comply with the standard Java naming specification, the argument identifier should be NOTEX, which can be separated from the class name. In Tune, "InstrumentX I" will issue a Play () message while using a NOTEX member as an argument (Middle_C). Since Notex contains int definitions, the INT version of the overloaded play () method will be called. At the same time, since it has not been "overwritten", the basic class version is used.

The output is:

InstrumentX.play ()

7.4 Abstract and Method

In all of our instruments (Instrument), the methods within the basic type of Instrument are definitely a "pseudo" method. If you want to call these methods, there will be an error. That is because the intent of Instrument is to create a general interface for all classes derived.

The reason why this general interface is to be established is that it can make different representations for different subtypes. It has established a basic form for us to define something "universal" in all derivatives. In order to explain this concept, another method is to refer to INSTRUMENT as "abstract basic class" (referred to as "abstract class"). If you want to process a series of classes through this general interface, you need to create an abstract class. For all derivatives that match the signature of the basic class declaration, they can be called by dynamic binding mechanisms (however, as indicated in the previous section, if the method name is the same as the base class, but the argument or parameters are different. Overloading occurs, then it is not what we are willing).

If there is an abstract class like INSTRUMENT, the object of that class is almost certainly no matter. In other words, the role of Instrument is just an expression interface, rather than expressing some specific implementation details. So create an Instrument object is meaningless, and we usually do it from doing users. To achieve this purpose, all methods in Instrument will display an error message. However, this will delay the information to the running period and require a thorough, reliable test in the user. In any case, the best way is to capture problems during compilation.

In response to this problem, Java specializes in a mechanism called "abstract method." It belongs to an incomplete method, which contains only a statement, there is no method main body. The following is the grammar used when the abstract method declares:

Abstract void x ();

A class containing an abstract method is called "abstract class". If a class contains one or more abstract methods, the class must be specified as Abstract. Otherwise, the compiler will report a mistake message to us.

If an abstract class is incomplete, once someone tries to generate an object of that class, what action will the compiler take? Since it is unable to create an object belonging to an abstract class, a error prompt is obtained from the compiler. In this way, the compiler guarantees "pureness" of abstract classes, we don't have to worry about it. If you inherit from an abstract class, and you want to generate an object of a new type, you must provide a method to define all abstract methods in the underlying class. If you don't do this (you can choose not to do it), the derivative class will also be abstract, and the compiler will force us to use the Abstract keyword flag of "abstract" essentity.

Even if there is no ABSTRACT method, a class can be declared into "abstract classes". If a class does not have to have any abstract method, and we want to ban all instances of the class, this ability will look very useful.

The INSTRUMENT class can easily convert into an abstract class. Only some of the methods will become an abstract method, because after a class abstraction, it does not force us to turn all the methods simultaneously into abstraction. Here is what it looks like:

266 pages

Below is the example of "Orchestra" instrument we have modified, with abstract classes and methods:

Page 266-268

It can be seen that there is no change in addition to the basic class.

Creating abstract classes and methods are sometimes useful to us because they make a clear fact that the abstraction becomes obvious, and it can clearly tell the user and the compiler to use it.

7.5 interface

"Interface" keyword makes abstract concepts deeply into a layer. We can imagine it as a "pure" abstraction class. It allows the creator to specify a basic form of a class: method name, argument list, and return type, but does not specify the method main body. The interface also includes data members of the basic data type, but they are default to static and final. The interface only provides a form without providing details.

The interface describes yourself: "For all the classes of my class, it should look like I now." Therefore, all code to use a particular interface know what method may be called for that interface. This is the all meaning of the interface. So we often use interfaces to build a "agreement" between classes and classes. Some object-oriented programming languages ​​use a keyword called "protocol" (protocol), which is the same as the interface.

To create an interface, use the interface keyword without using the Class. Similar to the class, we can add a public keyword in front of the interface keyword (but only the interface is defined within one file of the same name); or omitting it, create a "friendly" state.

In order to generate a class that is consistent with a specific interface (or a set of interfaces), use the imports keyword. What we want to express is that the interface looks like that look, here is the specific work details. " In addition to these, our other work is very similar to inheritance. The following is a schematic of the instrument example:

269 ​​pages

After the interface is implemented, a normal class is obtained, which can be extended in a standard manner.

Determine the declaration of the method in an interface to be explicitly defined as "public". But even if it is not clear, they will default to public. So when implementing an interface, the method from the interface must be defined as a public. Otherwise, they will default to "friendly", and will limit our access to a method during inheritance - Java compiler does not allow us to do.

In the modified version of the Instrument example, you can see this clearly. Note Each method in the interface is strictly a statement, which is the only allowed compiler. In addition, there is no method in Instrument5 being declared as public, but they all automatically get the public attribute. As shown below: Page 270-271

The residual part of the code works in the same way. We can use the "normal" class named Instrument5, a "abstract" class named Instrument5, or an "interface" named Instrument5. All behaviors are the same. In fact, we can find that there is no evidence in the tune () method to show that INSTRUMENT5 is a "ordinary" class, "Abstract" class is also an "interface." This is done intention: each method makes programmers to perform different controls for the creation of objects.

7.5.1 Java's "Multiple Inheritance"

The interface is just a form of "more pure" than abstract classes. Its use does not stop those. Since the interface does not have a specific implementation details - that is, there is no association with the storage space and "interface" - so there is no way to prevent multiple interfaces from merged together. This is crucial because we often need to express this: "X belongs to A, which is also from B, which belongs to C". In C , the actions combined with multiple classes are called "multiple inheritance", and the operation is more inconvenient, because each class may have a set of implementation details. In Java, we can take the same actions, but only one of the classes have specific implementation details. So when you merge multiple interfaces, the problem with C will not repeat them in Java. As follows:

272 pages

In a derivative class, we do not have to have an abstract or specific (there is no abstract method) of the base class. If you really want to inherit from a non-interface, you can only inherit from one. All of the remaining basic elements must be "interface". We placed all interfaces behind the Implements keyword and divide them with commas. Multiple interfaces can be used as needed, and each interface will become a separate type, which can be traced back. The following example shows a "specific" class with several interfaces, which eventually generates a new class:

Page 272-273

As can be seen, HERO combines the specific class Actioncharacter with the interface Canfight, Canswim, and Canfly. When merging a specific class with the interface according to this form, the specific class must appear first, and then the interface (otherwise the compiler will report an error).

Note that the signature of the front () is the same in the Canfight interface and the ActionCharacter class, and there is no specific definition for Fight () in Hero. The rules of the interface are: We can inherit from it (you will see later), but this will be another interface. If you want to create an object of a new type, it must be a class that has been provided all definitions. Although Hero does not explicitly provide a definition, it is defined as ActionCharacter, so this definition will be provided, we can create Hero objects.

In class Adventure, we can see a total of four ways, which use different interfaces and specific classes as their own arguments. Once you have created an HERO object, it can be passed to any of these methods. This means that they are traced back to each interface. Since the interface is designed with Java, there will be no problem in doing this, and the programmer does not have to pay any special attention to this.

Note that the above example has revealed the most critical role of the interface and the most important reason for the interface: can be traced to multiple basic classes. The second reason for using the interface is the same as the use of abstract basic classes: preventing a customer programmer from making this type of object, and stipulating it is just an interface. This brings a problem: What should I use an interface or an abstract class? If you use an interface, we can get the benefits of abstract classes and interfaces. So if you want to create the basic class that doesn't have any method or member variables, then you are willing to use the interface, don't choose an abstract class. In fact, if you know something in advance, the first choice is to turn it into an interface. The abstract class should only be considered if the method definition or member variable must be used. 7.5.2 Extended interface by inheritance

With inheritance technology, it is convenient to add a new method declaration for an interface, and several interfaces can be combined into a new interface. In both cases, the final result is a new interface, as shown in the following example:

Page 274-275

DangerousMonster is a simple extension to Monster and eventually generates a new interface. This is achieved in Dragonzilla.

Vampire's syntax can only be used when inheriting the interface. Typically, we can only apply an Extends keyword for a single class. However, since the interface may consist of multiple other interfaces, Extends may reference multiple foundations when building a new interface. As everyone saw, the name of the interface simply separated by commas.

7.5.3 constant group

Since all fields that are placed have a STATIC and FINAL properties, the interface is a good tool for grouped constant values, which has very similar effects with C or C ENUM. As shown in the following example:

Page 275-276

Note that the Static Final basic data type (i.e., the compile period constant) with a fixed identifier is all in terms of uppercase letters (multiple words in the single identifier).

The fields in the interface automatically have a public property, so it is not necessary to specify.

Now, by importing C07. * Or c07.months, we can use the ordinary use of the package from the package - just like other of the other packets. In addition, values ​​can also be referenced in a representation of the expression similar to MONTHS. JANUARY. Of course, we are just an int, so you don't have additional types of security as C enum. But compared to the number of forced numbers (hard coding) into their own procedure, this (common) technology is undoubtedly a huge progress. We usually refer to the "hardcod" number of "magic numbers", and the code it produces is very difficult to maintain.

If you don't want to give up additional type of security, you can build a class below (notes 1):

Page 276-277

1: It is the inspiration of Rich Hoffarth triggered me to write the program like this.

This class is called MONTH2 because there is already a month in the standard Java reservoir. It is a Final class and contains a private builder, so no one can inherit from it, or an instance of it. The only instance is those Final Static objects, which are created inside the class itself, including: Jan, Feb, Mar, etc. These objects are also used in the month array, and the latter allows us to pick up the month by digital, not by name (note that the array provides an extra JAN, making the offset increased by 1, which also makes the december indeed be December) . In Main (), we can notice the type of security: m is a MONTH2 object, so it can only be assigned to Month2. In the previous MONTHS.JAVA example, only the int value is provided, so I originally want to use an int variable that can actually get an integer value, which may not be very safe. The methods described here also allow us to exchange == or equals (), just like the main () tail display.

7.5.4 Initializing the fields in the interface

The fields defined in the interface automatically have STATIC and FINAL properties. They can't be "blank Final", but can initialize a very number expression. E.g:

Page 277-278

Since the field is static, they will get initialization after the first loading class, and before the first time you visit any field. Here is a simple test:

Page 278

Of course, the field is not part of the interface, but is saved in the Static storage area of ​​that interface.

7.6 internal class

In Java 1.1, you can place a class definition into another class definition. This is called "internal class". Internal classes are very useful for us because it can groups those who logically interconnect them and control a "visibility" class in another class. However, we must recognize that there is an fundamental difference between the internal class and the "synthetic" method previously described.

Typically, the need for internal classes is not particularly obvious, at least not immediately feel that the internal class needs to be used. At the end of this chapter, you will find a special example after all the syntax of the internal class. By it should clearly recognize the benefits of the internal class.

The process of creating an internal class is dull: Place the class definition into a class inside the class for encapsulation (if you have trouble, please refer to Chapter 3 3.1.2 "Assignment":

Page 278-279

If you use it within Ship (), the use of the internal class does not look at any other classes. Here, the only distinction is that its name is nesting in PARCEL1. But soon, I will know that this is not the only difference.

One more typical situation is that an external class has a special method that returns a handle pointing to an internal class. Just like this:

Page 279-280

If you want to generate an internal class in any place within the outside of the external class, it is necessary to set the type of object to "external class name. Internal class name", as shown in main ().

7.6.1 Internal Class and Trace

So far, the internal class looks still nothing special. After all, use it to hide a small question. Java already has a very good hidden mechanism - only allows classes to be "friendly" (visible only in a package), not to create an internal class.

However, when we are ready to stand up to a basic class (especially to an interface), the internal class begins to play its key role (from the object to be implemented to generate an interface handle with the same shape to a base class. effect). This is because the internal class can then enter the invisible or unavailable state - all of them. So we can hide the implementation details very conveniently. All of our rewards is a basic class or interface handle, and it is even possible to know accurate type. Just like this: 280-281 Page Program

Now, Contents and Destination representatives can be used by the client programmer (remember that all all members will turn all the members into public properties). For convenience, they are placed in a single file, but the original Contents and Destination are mutually PUBLIC in their own files.

In PARCEL3, some new things have been added: Internal class PContents is set to Private, so anything else cannot access it in addition to Parcel3. PDESTINA is set to protected, so in addition to the class in PARCEL3, PARCEL3 packs (because protected has given access to the package; that is, protected is also "friendly"), and other things outside of Parcel3, else all things You cannot access PDestination. This means that the customer programmer will be restricted to the understanding and access to these members. In fact, we can't even discontinitate to a private internal class (or a protected internal class unless you are a successor), because we can't access the name, just like it is seen in Classtest. Therefore, using the Private internal class, class designers can completely ban others depend on type encoding, and can completely hide the specific implementation details. In addition, from the perspective of the customer programmer, the scope of an interface is meaningless because they cannot access any additional methods that are not belonging to the public interface class. In this way, the Java compiler also has the opportunity to generate a higher efficiency code.

Ordinary (non-internal) classes are not available to private or protected - only public or "friendly" is allowed.

Note that Contents does not have to be an abstract class. You can also use a common class here, but this most typical starting point is still an "interface."

7.6.2 Internal classes in the method and scope

At this point, we have basically understood the typical use of the internal class. For those who involve internal classes, usually express "simple" internal classes, very simple, easy to understand. However, the design of the internal class is very comprehensive and inevitably encounters their other large usage - false, if we create internal classes in one way or even an arbitrary scope. There are two reasons that prompted us to do this:

(1) As mentioned earlier, we are ready to implement some form of interface, so that you can create and return a handle.

(2) To solve a complex problem and want to create a class to assist yourself. At the same time, I don't want to disclose it.

In the following example, you will modify the previous code for use:

(1) Class defined within a method

(2) Class defined in a scope of the method

(3) An anonymous class for implementing an interface

(4) An anonymous class that extends a class with non-default builder

(5) An anonymous class for performing field initialization

(6) An anonymous class, constructs by instance initialization (anonymous internal classes cannot have builder)

All of this happened in the inNerscopes package. First, universal interfaces from the aforementioned code will get definitions in their own files, allowing them to use in all examples: 283 pages

Since we already think that Contents may be an abstract class, you can take the following more natural form, just like an interface:

283 pages

Although it is a common class that contains specific implementation details, Wrapping also uses a general "interface" of all of its derivatives:

283-page program

In the above code, we noticed that wrapping has a builder that requires the use of an argument, which makes the situation more interesting.

The first example shows how to create a complete class in a scope of a method (rather than another class):

284 page

The PDestination class belongs to a part of DEST () instead of part of PARCEL4 (pay attention to an internal class PDESTINATION that can be used inside each class in the same directory, which does not have naming conflicts). Therefore, PDESTINATION cannot be accessed from DEST (). Note that the tracery occurring in the return statement - except for a handle pointing to the basic class Destination, there is no thing outside the DEST () boundary. Of course, you can't put it in DEST () because the name of the PDESTINATION is considered to be DEST (), which is not a valid object.

The following example shows how to nested an internal class in any scope:

284-285 Page 2

The Trackingslip class is nesting in the scope of an IF statement. This doesn't mean that classes are creative - it will compile with all other things. However, it is not available outside of the action domain that defines it. In addition to these, it doesn't seem to differ from a common class.

The following example looks a bit weird:

285-page program

The CONT () method simultaneously combines the creation code of the return value, and the class used to represent the return value. In addition, this class is anonymous - it has no name. And it seems that it seems more to be more mind, we are ready to create a Contents object:

Return New Contents ()

But after this, we said: "Wait until the first, let me first play in a class definition":

Return new contents () {

Private INT i = 11;

Public int value () {Return I;}

}

This strange syntax should express the meaning: "Create an object from the anonymous class derived from Contents." The handle returned by the New Expression will be tracered into a Contents handle. The syntax of an anonymous internal class is actually to express:

Class mycontents extends contents {

Private INT i = 11;

Public int value () {Return I;}

}

Return new mycontents ();

In an anonymous internal class, Contents is created with a default builder. The following code shows what the basic class needs to be made when the builder contains the argument:

Page 286-287

That is, we simply transmit appropriate self-variables to the base class builder, which is manifested in "New Wrapping (X)". Anonymous classes cannot have a builder, which is different from the general practices when you call Super ().

In the two examples described above, the semicolon does not mark the end of the primary body (and C ). Instead, it marks the end of the expression that contains anonymous class. Therefore, it is completely equivalent to using a semicolon anywhere. If you want to initialize some form of an object of anonymous internal classes, what happens? Since it is anonymous, there is no name to the builder, so we can't have a builder. However, we can initialize when defining your own fields:

287 page

If you try to define an anonymous internal class, you want to use an object externally defined in an anonymous internal class, the compiler requires an external object to be final properties. This is why we set DEST ()'s own arguments to Final. If you forget this, you will get a compile time error prompt.

As long as you just want to assign a field, the above method is sure. But if you need to take some action similar to the builder, what should I do? Initialization through the instance of Java 1.1, we can effectively create a builder for an anonymous internal class:

288 pages

In the instance initialization module, we can see that the code cannot be executed as part of the class initialization module (ie, if statement). So actually, an instance initialization module is an anonymous internal class builder. Of course, its function is limited; we cannot overload the instance initialization module, so it can only have one of these builders.

7.6.3 Link to the external class

So far, the internal class we have seen is just a name hidden and code organization. Although these functions are very useful, it seems not particularly eye-catching. However, we also ignored another important fact. When you create your own internal class, the object of that class has a link to the package object (these object packages or generated internal classes). So they can access members of the package - no eligibility eligibility. In addition, the internal class has access to all elements of the package (annotation 2). The following example explains this question:

289-290 page program

2: This is quite different from C "nested" design, which is just a simple name hidden mechanism. In C , there is no link to a package object, and there is no default access.

Where SEQUENCE is just a size fixed object array, there is a class to encapsulate it inside. We call Add () to add a new object to the end of Sequence (if there is also a place). In order to get every object in Sequence, use a interface called Selector, which enables us to know if he is at the end (end ()), can watch the current object (Current () object), and movable to Sequence The next object (Next () Object). Since the Selector is an interface, many other classes can be implemented in their own way, and many methods can use the interface as an argument to create a general code.

Here, the SSELector is a private class that provides the Selector function. In Main (), you can see the Sequence creation process, behind it is the addition of a series of string objects. Subsequently, a Selector is generated by a call to getSelector (). And use it to move in Sequence while selecting each item.

From the surface, SSELECTOR seems to be just another internal class. But don't be confused by the surface phenomenon. Note that end (), current () and next () are observed, each of which references O. o is a handle that is not part of the SSELector, but a private field in the package class. However, internal classes can access methods and fields from the package class, just like they have already owned them. This feature is very convenient for us, just like it is seen in the example above. Therefore, we now know an internal class to access members of the package class. How is this implementation? The internal class must have a reference to a particular object of the package class, and the role of the package class is to create this internal class. Subsequently, when we reference a member of the package, you will use the (hidden) reference to select that member. Fortunately, the compiler will help us take care of all of these details. But we can now understand an object of the internal class can only be created in combination with an object of the package class. During this creation process, the handle of the package class object is required to initialize. If you cannot access that handle, the compiler will report an error. When all of these operations, most of the time is not required to intervene.

7.6.4 Static Internal Class

To correctly understand the meaning of STATIC in the internal class, you must remember that the internal class object default holds a handle of an object that creates it. However, if we say an internal class is static, this statement is not established. Static internal classes mean:

(1) In order to create an object of a Static internal class, we do not need an external class object.

(2) An external class object cannot be accessed from an object of the Static internal class.

But there are some restrictions: Since the Static member can only be in an external level of a class, the internal class cannot own STATIC data or STATIC internal classes.

If you create an object of an internal class, you can set all things to static. In order to work normally, the internal class must be set to Static. As follows:

291-292 page program

In Main (), we don't need the object of PARCEL10;

Typically, we don't set any code in an interface, but the STATIC internal class can be part of the interface. Since the class is "static", it does not violate the rules of the interface - the inner class is located only within the interface of the interface:

292 pages

Earlier in this book, I suggest that you set a main () in each class, use it as the test bed for that class. A disadvantage of this is that there are too many additional code. If you don't want this, you can consider your test code with a Static internal class. As follows:

292-293 page program

This generates a separate, class called Testbed $ Tester (for running, please use the Java Testbed $ Tester "command). This class can be used to test, but do not need to include it in your final release.

7.6.5 Reference External Class Object

If you want to generate the handle of an external class, you must use a bit and a this to name an external class. For example, in the Sequence.Sselector class, all of its methods can generate the stored handle of external class sequence, and the method is in the form of sequence.this. The result obtained the handle will automatically have the correct type (this will check and verify during the compilation, so there will be no cost period.

Sometimes, we want to tell other objects to create an object of an internal class. To achieve this, a handle pointing to other external classes must be provided in the New expression, just like this: 293-294 page

In order to create an object of the internal class, you can't like everyone may guess - use the same form and reference the external class name PARCEL11. At this point, an object of an external class must be utilized by an external class:

PARCEL11.CONTENTS C = P.NEW Contents ();

Therefore, an object of an internal class is not possible unless an object has an external class. This is because the internal class object has been connected to the object "silently" of the object that creates its external class. However, if a STATIC internal class is generated, it is not necessary to point to a handle of the external class object.

7.6.6 Inheriting from internal class

Since the internal class builder must contact a handle of the encapsulation class, the situation will be slightly complex when inheriting from an internal class. The problem here is that the "secret" handle of the package must be initialized, and no longer a default object can be connected in the derivative class. The way to solve this problem is to adopt a special grammar, clearly establish this association:

294-295 page program

It can be seen from it, inheritinner extension only, does not extend the external class. But when you need to create a builder, the default object has no meaning, we can't just pass a handle of the package object. In addition, the following syntax must be used in the builder:

EnclosingClasshandle.super ();

It provides the necessary handles so that the program is correctly compiled.

7.6.7 Can the internal class be overwritten?

What happens when you create an internal class and then inherit from the package class and redefine the internal class? In other words, are we possible to cover an internal class? This seems to be a very useful concept, but "override" an internal class - seems to be another method of the external class - this concept does not do anything:

295-296 page program

The default builder is automatically synthesized by the compiler, and the default builder of the base class will be called. Everyone may think that since it is ready to create a BiGeGG, you will use YOLK's "overwrite" version. But the actual situation is not. The output is as follows:

New Egg ()

Egg.yolk ()

This example simply reveals that when we inherit from the outside, there is no additional internal class to continue. However, it is still possible to "clear" inheritance from the internal class:

296-297 page program

Now, BiGegg2.yolk explicitly expands Egg2.yolk and covers it. Method INSERTYOLK () allows BiGegg2 to be traced back to the Y handle of EGG2 in a certain YOLK object. So when g () is called Y.f (), F () is overwritten. The output is as follows:

Egg2.yolk ()

New egg2 ()

Egg2.yolk ()

Bigegg2.yolk ()

Bigegg2.yolk.f ()

The second call for Egg2.yolk () is the basic class builder call for the Bigegg2.Yolk builder. transfer

When g (), it can be found that F () covered version is used.

7.6.8 Internal class identifier

Since each class generates a .class file, it is used to accommodate all the information related to how this type of object (this information produces a class class named Class object), so everyone may guess internal classes. The corresponding .class file must be generated to accommodate information related to their Class objects. These files or classes of the names abide by a strict form: first is the name of the package, follow one, follow the name of the internal class. For example, the .class file created by inheritinner.java includes: inheritinner.class

WITHINNER $ INNER.CLASS

WITHINNER.CLASS

If the internal class is anonymous, the compiler simply generates numbers and uses them as internal class identifiers. If the internal class is nested in other internal classes, their names are simply appended behind a $ and the external class identifier.

This method of generating internal names is also very simple and intuitive, and it is also very "robust" to accommodate most of the requirements (notes 3). Since it is a standard naming mechanism of Java, the resulting file will automatically have the ability to "unrelated to the platform" (Note that the Java compiler changes the internal class according to the situation so that it can work normally in different platforms).

3: But on the other hand, since "$" is also a metamor of the UNIX housing, sometimes it will be troublesome when it lists .class files. Take this program to be strange to a UNIX-based company - SUN. My guess is that they don't carefully consider this issue, but think we will put all the attention naturally on the source file.

7.6.9 Why use internal classes: control framework

So far, everyone has contacted a lot of grammar and concepts described in the operation of internal classes. But these don't truly explain the reasons for the internal class. Why is Sun to add such a basic language feature in Java 1.1 so troublesome? The answer is what we have to learn here.

A "application framework" refers to one or a series of classes that specifically designed to solve specific types of issues. For the application framework, we can inherit from one or more classes and override some of them. The code we have written in the overlay method is used to customize the regulations provided by those application frameworks to solve their own actual problems. "Control Framework" belongs to a special type of application framework, which is protected by the needs of the event response; the system is mainly used to respond to the event is called "system-driven system". In the application design language, one of the most important issues is "Graphical User Interface" (GUI), which is almost entirely driven by events. As everyone will learn in Chapter 13, Java 1.1 AWT belongs to a control framework that perfectly solves the GUI problem through internal classes.

To understand how internal classes simplify the creation and use of the control framework, you can think that a control framework is to perform them after the event "Ready". Although "ready" means a lot, but in this case, we are based on computer clock. Subsequently, realize that something that needs to be controlled for the control framework is not included in the framework. First, it is a special interface that describes all control events. It can be an abstract class rather than an actual interface. Since the default behavior is based on time control, some implementation details may include:

299 page

When you want Event to run, the builder simply captures time. At the same time, ready () tells us when to run it. Of course, ready () can also be covered in a derivative class and establish an event to other things other than time.

Action () is a method that needs to be called after the event is ready, and Description () provides text information related to the event. The following file contains the actual control framework for managing and triggering events. The first class is actually just a "assistant" class, and its responsibility is to accommodate the Event object. It can be replaced with any suitable collection. And through the study of Chapter 8, everyone will know that other collections can simplify our work, do not need we write these extra code:

299-300 Page Program

Eventset can accommodate 100 events (if you use a "real" collection from Chapter 8 here, you don't have to worry about its maximum size because it will automatically change the size as appropriate). Index is used here to track the next available space, and next help us find the next event in the list, understand whether he has been looped. This is critical in the call to getNext (), because once run, the Event object will be deleted from the list (using removecurrent ()). So getNext () will encounter "empty cave" when moving forward in the list.

Note that removecurrent () does not just indicate some flags, pointing out that the object is no longer used. Instead, it sets the handle to NULL. This is very important because if the garbage collector finds a handle to still use it, it will not clear the object. If you think that your handle may be hang like this, it is best to set it to NULL, so that the garbage collector can clear them normally.

Controller is where practical work. It accommodates your EVENT object with an eventset, and addevent () allows us to join this list to this list. But the most important method is Run (). This method will traverse in EventSet and search for an EVENT object that is ready to run - READY (). For every object it finds ready (), you will call the action () method, print the description (), and then delete the event from the list.

Note that in all the designs so far, we still can't accurately know what "event" is doing. This is the key to the entire design; how do it "will distinguish between things that have changed the same thing"? Or use me, "changed intent" caused different actions of various EVENT objects. We express different actions by creating different EVENT subclasses.

Here is the place where the internal class shows. They allow us to do two things:

(1) Expressing all of the implementation details of a control frame application in a single class, so that all things related to the implementation are completely packaged. Internal classes are used to express a variety of different types of action (), which are used to solve practical problems. In addition, the subsequent example uses the Private internal class, so implementing details will be completely hidden and can be safely modified.

(2) The internal class makes our specific implementation more clever because any member of the external class can be easily accessed. If you don't have this ability, the code may seem to make people feel comfortable, and finally I have to find other ways to solve.

Now I want to think about a specific embodiment of the control framework, it is designed to control the Greenhouse function (annotation 4). Each action is completely different: control light, water supply, and temperature automatic adjustment opening, control bell, and restart the system. However, the design of the control framework is to easily isolate different code. For each type of action, you must inherit a new EVENT internal class and write the corresponding control code in ACTION ().

4: Due to certain special reasons, this is a very interesting question that often needs to be solved. The original example has appeared in the "C Inside & Out" book, but Java provides a more order People comfortable solution. As a typical behavior of the application framework, the GreenhouseControls class is inherited from Controller:

302-305 page program

Note Light (light), Water (water supply), thermost (warming), and Rings are part of the external GreenhouseControls, so the internal class can access those fields without obstruction. In addition, most ACTION () methods also involve certain form of hardware control, which usually requires calls to non-Java code.

Most Event classes look similar, but Bell (bell) and Restart are special. Bell will make a sound, if there is no one, it will add a new Bell object in the event list, so the bell will rest later. Note that the internal class looks similar to multiple inheritance: Bell has all methods of Event, and there is also all methods of external GreenhouseControls.

Restart is responsible for initializing the system, so all necessary events will be added. Of course, a more flexible approach is to avoid "hard coding", but from one file (one exercise in Chapter 10, you will ask you to modify this example, thereby achieving this goal). Since Restart () is just another EVENT object, you can also add a RESTART object in Restart.Action () to enable the system to restart regularly. In Main (), all things we need to do is to create a GreenhouseControls object and add a RESTART object to work.

This example should make everyone a more profound understanding of the value of the internal class, especially when using them in a control framework. In addition, in the second half of Chapter 13, you will also see how to subtly use the internal class to describe a graphical user interface behavior. After completing the study, the understanding of the internal class will rise to an unprecedented new height.

7.7 Builder and Polymourne

As usual, the builder is different from other types of methods. This method is still established after the problem involving the velocity. Although the builder does not have versatile (even if a "virtual builder" - will be introduced in Chapter 11), it is still necessary to understand how the builder is in complex hierarchical structure and with the same velocity. use. This understanding will help you avoid some unpleasant disputes.

7.7.1 Call order of the builder

The order in which the builder call has been briefly described in Chapter 4, but it is before the inheritance and versatile issues.

Builders for the basic class are certainly called in a derived class builder, and gradually link to enable the builder for each base class to be called. The reason why the reason is to do is because the builder has a special task: check whether the object has been constructed correctly. A derivatized class can only access its own members and cannot access the basic class members (these members usually have a private property). Only the basic class builder knows the correct method and has appropriate permissions when initializing their own elements. Therefore, you must make all builders to be called, otherwise the construction of the entire object may be incorrect. That is exactly why the compiler is forced to build a builder to build a constructor. In the constructor main body of the derived class, if we don't explicitly specify the call to an underlying class builder, it will "silently" the default builder. If there is no default builder, the compiler will report an error (if a class does not have a builder, the compiler will automatically organize a default builder). Let's take a look at an example, which shows the effects of synthesis, inheritance, and velocity of the build order:

306-307 page program

This example creates a complex class outside of other classes, and each class has a builder announced by yourself. The most important classes are Sandwich, which reflects three levels of inheritance (if the default amount of Object is inherited, it is four-level) and three member objects. After the main () created a Sandwich object, the output is as follows:

307-308 page program

This means that for a complex object, the buffer call follows the following order:

(1) Call the underlying class builder. This step will continue to repeat, first get the root of the hierarchical structure, then the next derived class, and so on. Until the derivative class of the most deepest layer.

(2) Call the member initialization module in the order of declaration.

(3) The main body of the derivative builder is called.

The order in which the builder call is very important. When inheriting, we know everything about the basic class and access any public and protected members of the basic class. This means that when we are derived, you must assume that all members of the basic class are valid. With a standard method, build actions have been carried out, so members of all parts of the object have been built. But inside the builder must ensure that all members used are constructed. In order to achieve this requirement, the only way is to first call the underlying class builder. Then after entering the derivative builder, all members we can access in the basic class have been initialized. In addition, all member objects (which are objects in the class) (such as B, C, and L) in the above example, because we should initialize them as much as possible, so All members inside the builder should also be guaranteed to be effective. If you adhere to this rule, we will help us determine that all basic class members and members of the current object have been properly initialized. But unfortunately, this approach does not apply to all situations, which will be specified in the next section.

7.7.2 Inheritance and Finalize ()

When you create a new class through the Synthesis method, you will never worry about the end of the member object of that class. Each member is an independent object, so it will get normal garbage collection and ending processing - no matter whether it is a member of a class. However, when performing initialization, it is necessary to cover the Finalize () method in the derived class - if a special clear process has been designed, it is required to be part of the garbage collection. When you override Finalize () of the derived class, be sure to remember the basic class version that calls Finalize (). Otherwise, the initialization of the basic class will not occur at all. The following example is that it will:

309-311 page program

The DobaseFinalization class simply accommodates a flag, indicating whether sar.finalize () should be called to each class in the hierarchical structure. The setting of this flag is based on the command line parameters, so it is possible to view the behavior under the premise that the basic class finishing work is made. Each class in the grading structure also includes a member object of the Characteristic class. Everyone can see that the CHARACTERISTIC member object will definitely obtain the end (clear) processing regardless of whether the underlying type finished module is called.

Each covered Finalize () must have access to protected members, because the Finalize () method in the Object class has a protected property, and the compiler does not allow us to eliminate access during inheritance ("Friendly" ratio "Protected" has smaller access rights).

In frog.main (), the DobaseFinalization flag will be configured and create a separate Frog object. Remember that garbage collection (especially the end) may not occur for any particular object, so in order to force this action, System.RunfinalizersoneXit (TRUE) adds additional overhead to ensure the normal operation of the end. If there is no basic class initialization, the output result is:

311 page

It can be seen from it indeed not call the final module for the basic class Frog. However, if the "femalize" independent variable is added to the command line, the following results are obtained:

311-312 page program

Although the member object is tailing in the same order in the same order as they created, from the technical point of view, there is no designation of the object. But for the basic class, we can control the sequence of ends. The best order employed is the order in which it is used here, and it is opposite to the initialization order. According to the same form as "destroyer" in C , we should first perform the end of the derived class, and then the end of the basic class. This is because the derivative class may call the same method in the base class, requiring the basic class component to still be active. Therefore, they must be removed (destroyed) in advance.

7.7.3 Behavior of a multi-portable method within the builder

The hierarchical structure (order) called us a interesting question, or let us enter a situation in which it is difficult to retreat. If the current is currently located inside the builder, what will happen when you call a dynamic binding method of the object that is prepared? Inside the original method, we can imagine what happens - the dynamic binding call will parse during the run, because the object does not know that it is from the class belonging to the method, or from it derived from it Some classes. In order to maintain consistency, everyone may think that this should occur inside the builder.

But the actual situation is not complete. If a method of dynamically binding within the builder is called, the definition of that method is covered. However, the resulting effect may not be as expected, and may result in some program errors that are difficult to discover.

Conceptually, the responsibility of the builder is to let the object actually enter the presence. In any builder, the entire object may just get some organizations - we only know that the basic class object has been initialized, but I don't know which classes have been inherited. However, a dynamic binding method call is "forward" or "outward" in a hierarchical structure. It calls a method located in the derived class. If this is done inside the builder, then a member of the call may not be properly initialized - it is obviously not what we hope.

By observing the following example, this problem will be clear:

313 page program

In GLYPH, the DRAW () method is "Abstract", so it can be overwritten by other methods. In fact, we have to cover it in roveglyph. But the Glyph builder calls this method, and the call will be in Roundglyph.Draw (), which seems to be intentionally. But see the output: 314 page

When the GLYPH builder calls DRAW (), the value of RADIUS is not even the default initial value 1, but 0. This may be caused by no paintings at all or on the screen. This has to start the findings in the search program, try to find out the reason why the program cannot work.

The initial order in the previous section is not very complete, and that is the key to solving the problem. The actual process of initialization is this:

(1) The storage space assigned to the object is initialized into binary zero before taking any other operation.

(2) Call the underlying class builder as in the previous description. At this time, the covered DRAW () method will be called (which is indeed before the Roundglyph builder calls), which will find that the value of the RADIUS is 0, which is caused by step (1).

(3) Call the member initialization code in the order in which the original statement is previously declared.

(4) Call the main body of the derived builder.

There is a premise of taking these operations, that is, all things should be initialized into zero (or some special data types and "zero" equivalent value), rather than just to leave garbage. This includes embedding an object handle inside the class via "synthetic" technology. If you forget to initialize the handle, you will have a violation event during the run. Everything else will become zero, which is usually a serious warning signal when viewing results.

On the other hand, the results of this procedure should be vigilant. From a logical perspective, we seem to have been invapable, so it is very incredible. And there is no error message from the compiler (C will show more reasonable behavior in this case). This error will be easily ignored, and it takes a long time to find it.

Therefore, a particularly effective rule when designing the builder is: use as possible to enable objects into the ready state; if possible, avoid calling any method. The only way to be securely invoked in the builder is those methods that have final properties in the base class (also available for the Private method, which automatically have the final property). These methods cannot be covered, so the above potential problems will not occur.

7.8 Design by inheritance

After learning the knowledge of the polymorbility, since the vectors are such a "smart" tool, it seems that all things should be inherited. But if you use inheritance technology, you will make your design unnecessarily complicated. In fact, when we establish a new category based on an existing class, if you first choose Inherit, it will make the situation abnormally complicated.

A better idea is to first select "Synthesis" - if it is not very sure which one should be used. Synthesis does not force our programming to enter inheritance. At the same time, the synthesis is more flexible because one type (and behavior) can be dynamically selected, while inheritance requires accurately known a type during compilation. The following example explains this:

315-316 page program

Here, a Stage object contains a handle pointing to an Actor, which is initialized into a HAPPYACTOR object. This means that Go () produces specific behaviors. However, since the handle can be rerouted or combined with a different object binding during operation, the handle of the Sadactor object can be replaced in A and then a behavior generated by Go () changes. In this way, we have gained great flexibility during operation. In contrast, we cannot inherit in different forms during operation; it requires it to be completely decided during the compilation period. A routine design guidelines are: use the difference between the behavior of inheritance, and express the state of the state variable. In the above example, both have been applied: two different classes are inherited to express the differences in the ACT () method; while the Stage allows its own state to change by synthetic techniques. In this case, the change in the state has also produced changes in behavior.

7.8.1 Pure inheritance and expansion

When studying inheritance, in order to create inheritance classification, it seems that the most obvious method is to take a "pure" means. That is, only the methods established in the basic class or "interface" can be covered in the derived class, as shown in this picture below:

316 pages

It can be described as a pure "belonging to" relationship, because a class interface has specified its "what" or "what is". By inheritance, all derivative classes can only have the basic class interface. If you operate on the above-described schematic, the derived class does not have anything else in addition to the basic class interface.

It can be imagined into a "pure replace" because the derivative object can be perfectly replaced by the base class. When using them, we don't have to know any additional information related to subclavab. As follows:

317 page

That is, the basic class can receive any message we send to the derived class because both have a fully consistent interface. All of us to do is from derived shape, and never need to go back to check the exact type of object. All details have been perfectly controlled by versions.

If you consider this idea, then a pure "belongs" relationship seems to be the only wise design method, any other design method will lead to confusion, and there is a big difficulty in definition. But this idea is another extreme. After meticulous research, we found that the expansion interface is a particularly effective solution for some specific issues. It is called "similar to" relationship because the extended derivative class "similar to" basic class - they have the same foundation interface - but it adds some features, requiring additional methods to implement. As follows:

318 page

Although this is a useful and sensible approach (by specific environmental decision), it also has a disadvantage: the part of the interface extension in the derivative class cannot be used in the base class. Therefore, once you go back, you can no longer call a new method:

318 page

Such problems will not occur if they don't touch it at this time. However, in many cases, you need to re-verify the exact type of the object, so that you can access the type of extension. In the subsequent section, we specify how this is achieved.

7.8.2 Trace style and running type identification

Since we lost specific type information during tracetability (in the inheritance structure), in order to obtain specific type information - ie move down in a hierarchical structure - we must use "Trace" technology . However, we know that it is definitely safe; the base class is impossible to have a larger interface than the derivative class. Therefore, each message sent by the basic interface will definitely be received. However, when we are still trace, we don't really know that a geometry is actually a circle, it is entirely a triangle, square or other shape.

319 pages

In order to solve this problem, there must be an approach to ensure that the stracker is correct. Only in this way, we will not take shape into a type of error and then issue a message that an object is impossible. This is very unsafe. In some languages ​​(such as C ), special operations must be taken in order to ensure "type security". But in Java, all models will automatically check and verify! Therefore, even if we just conduct a normal bracket shape, after entering the runtime, there will be no attention to this model without a message, ensuring that it is indeed the type we want. If not, you will get a ClassCastException (class styling violation). The behavior of checking the type during operation is called "Run Type Identity" (RTTI). The following example demonstrates the behavior of RTTI:

319-320 page program

As in the schematic, moreUseful (more useful) extends the useful (useful) interface. But because it is inherited, it can also be traced into a USEful. We can see that this occurs when the array x (located in main () is initialized. Because two objects in the array belong to the USEful class, the F () and g () methods can be simultaneously issued two. And if you try to call U () (it only exists moreUseful), a compile time error message will be received.

To access an extended interface of a moreuseful object, you can try a traceability. If it is the right type, this action will succeed. Otherwise, you will get a ClassCastException. We don't have to write any special code for this violation, because it pointed out a programming error that may occur anywhere in the program.

The significance of RTTI is much reflected in the styling. For example, before trying to track, you can understand what type itself is processed. The entire chapter 11 is in the face of the Java run type identification.

7.9 Summary

"Multi-shaped" means "different forms". In object-oriented programming, we have the same appearance (basic type of universal interface) and different forms of using that look: dynamically bind or organize, different versions.

Through this chapter, everyone knows that if you don't use data abstraction and inheritance technology, you can't understand, even an example of creating versions. Parallelism is an indispended feature (just like a Switch statement), only coordinating with other elements. We should look at it as part of the overall relationship. It is often confused from other Java, non-object-oriented features, such as methods overload, and these features sometimes have certain features targeted. But don't be fooled: If there is no binding in the future, it is not a conformal.

To use versatile or even object-oriented technologies, especially in their own procedures, they must extend their programming views to members and messages including alone, but also consistency between classes and classes. Relationship. Although this requires more effort to pay, it is very worthwhile, because only this can really effectively speed up your programming speed, better organize code, and make it easier to make a wide range of procedures and easier The code is maintained and expanded.

7.10 practice

(1) Creating a Rodent: mouse: mouse (mouse), Gerbil (mouse), Hamster (Big Checker) and the like. In the basic class, a method suitable for all RODENTs is provided, and they are covered in the derived class to take different actions according to different types of Rodent. Create a RODENT array where different types of RODENT is populated, then call your own basic class method to see what happens. (2) Modify the practice 1, so that RODENT becomes an interface.

(3) Reform the problem in Winderror.java.

(4) In GreenhouseControls.java, add the Event internal class to open and close the fan.

转载请注明原文地址:https://www.9cbs.com/read-4704.html

New Post(0)