Polymorphism is an important mechanism for object-oriented programming code reuse, and we have mentioned Java polymorphism more than once. In Java Runtime polymorphism: inheritance and implementation of interfaces in this article, we have described in detail the Java implementation of runtime polymorphism of dynamic method scheduling; Today we go deeper into the Java core and learn about the implementation of polymorphism in Java.
The term "polymorphism" comes from the Greek language, meaning "many forms". Most Java programmers consider polymorphism as an object's ability to invoke the correct version of the method. Nonetheless, this implementation-oriented view leads to the miraculous function of polymorphism, rather than merely taking polymorphism as a purely concept.
Polymorphism in Java is always polymorphic in subtypes. Almost mechanically produces a number of polymorphic behaviors that allow us not to consider the types of problems involved. In this paper, we study a kind of object-oriented viewpoint, and analyze how to separate the behavior of the object and the behavior that the object will behave. Aside from the notion that polymorphism in Java comes from inheritance, we can still feel that the interface in Java is a set of object sharing implementations without common code.
Classification of polymorphic
Polymorphism is a very common concept in object-oriented languages. Although we often confuse polymorphism, there are actually four different types of polymorphism. Before we begin to discuss the details of the formal subtype polymorphism, let's take a look at the polymorphism in the normal object-oriented form.
Luca Cardelli and Peter Wegner ("On understanding Types, Data abstraction, and polymorphism" author, The article refers to resource links) to divide polymorphism into two categories----specific and generic----four small classes: mandatory, overloaded, parametric, and inclusive. Their structure is as follows:
In such a system, polymorphism shows many forms of ability. Generic polymorphic references have a large number of objects of the same structure type, and they share common characteristics. A specific polymorphism involves a small number of objects that do not have the same characteristics. Four polymorphic states can be described as follows:
Mandatory: A method of implicitly doing type conversions.
Overloaded: Uses a marker as multiple meanings.
Parameter: Provides the same action for different types of parameters.
Contains: The class contains abstract operations of the relationship.
I'll give you a quick introduction to these polymorphic states before I tell you about subtype polymorphism.
Forcing polymorphism
Forcing a polymorphic implicitly converts a parameter into a type that the compiler considers to be correct in a way that avoids errors. In the following expression, the compiler must determine what the two-tuple operator ' + ' should do:
2.0 + 2.0
2.0 + 2
2.0 + "2"
The first expression adds the operands of the two double, which is specifically stated in Java.
The second expression adds a double type and an int. This operation is not explicitly defined in Java. However, the compiler implicitly converts the second operand to a double type and adds the double type. It is very handy for programmers, or it will throw a compilation error or force the programmer to explicitly convert int to double.
A third expression adds a double to a string. Such operations are also not defined in Java. So, the compiler converts a double into a string type and concatenates them.
Forcing polymorphism also occurs in a method call. Suppose the class derived inherits the class Base, Class C has a method, the prototype is M (Base), and in the following code, the compiler implicitly converts the object derived of the derived class to the object of the Base class. This implicit conversion enables the M (Base) method to use all parameters that can be converted to the base class.
- c C = new C ();
-
- Derived Derived = new Derived ();
-
- C.M (derived);
Also, implicit casts can avoid the hassle of type conversions and reduce compilation errors. Of course, the compiler will still give precedence to validating object types that conform to the definition.
Overloaded polymorphic
Overloading allows the use of the same operators or methods to represent distinct meanings. ' + ' in the above program has two meaning: two double type number added; two strings connected. In addition, there are integral types, long integers, and so on. The overloads of these operators depend on the compiler's choices based on the context. The previous compiler implicitly converts the operand to a type that fully conforms to the operator. Although Java explicitly supports overloading, user-defined operator overloads are not supported.
Java supports user-defined function overloading. A class can have a method of the same name, and these methods can have different meanings. In these overloaded methods, the number of parameters must be satisfied, and the parameter types at the same location are different. These differences can help the compiler distinguish between different versions of the method.
The compiler represents different methods with this uniquely represented feature, which is more efficient than the name representation. Thus, all polymorphic behavior can be compiled and passed.
Both forced and overloaded polymorphism are categorized as specific polymorphic, because these polymorphism are in a particular sense. These features, which are zoned into polymorphism, bring great convenience to programmers. Forced polymorphism excludes troublesome types and compilation errors. Overloaded polymorphic like a piece of sugar that allows programmers to represent different methods with the same name is convenient.
Polymorphism of parameters
Parameter polymorphism allows many types to be abstracted into a single representation. For example, in the list abstract class, a set of objects with the same characteristics is described, providing a common template. You can reuse this abstract class by specifying a type. These parameters can be any user-defined type, and a large number of users can use this abstract class, so parameter polymorphism is undoubtedly the most powerful polymorphic.
At first glance, the above abstract class seems to be a function of java.util.List. However, Java does not actually support true security type-style parameter polymorphism, which is why other collection classes of Java.util.List and Java.util are written with the original Java.lang.Object (refer to my article "A primordial Interface? "for more details). The single-root inheritance of Java solves some problems, but does not play the full function of the parameter polymorphism. Eric Allen has a wonderful article "Behold the Power of parametric polymorphism", which describes the Java generic types of requirements and recommends to Sun's Java specification requirements #000014 document "Add Generic Types to the Java programming Language. " (Refer to Resources link)
The included polymorphic
A polymorphic behavior is implemented by a type that contains polymorphic values and the inclusion relationship of a collection. In many object-oriented languages, including Java, the inclusion relationship is a subtype. Therefore, Java's inclusion polymorphism is a subtype of polymorphism.
In the early days, the polymorphism referred to by Java Developers was specifically the polymorphism of subtypes. With a type-oriented perspective, we can see the powerful features of subtype polymorphism. In the following article we will look at this issue carefully. For the sake of brevity, the polymorphic in the following refers to polymorphism.
Type-oriented perspective
The UML class diagram in Figure 1 gives a simple inheritance relationship of classes and types to facilitate the interpretation of polymorphic mechanisms. The model contains 5 types, 4 classes, and one interface. Although UML is called a class diagram, I think of it as a type diagram. As described in the article "Thanks type and Gentle class," Each class and interface is a user-defined type. Each rectangle in an independent implementation, such as a type-oriented view, represents a type. From the realization method, four kinds of types use the structure of the class, a structure that uses the interface.
The following code implements each user-defined data type, and I write the implementation very simply.
With this type declaration and the definition of the class, figure 2 describes the Java directive from a conceptual point of view.
Derived2 derived2 = new Derived2 ();
The Derived2 object is declared above, which is the Derived2 class. The topmost layer of Figure 2 depicts the Derived2 reference as a collection of Windows, although the Derived2 object beneath it is visible. This leaves a hole for each operation of the DERIVED2 type. Each operation of the Derived2 object maps the appropriate code, as described in the preceding code. For example, the Derived2 object maps the M1 () method defined in derived. The M1 () method of the base class is also overloaded. A Derived2 reference variable does not have permission to access the overloaded M1 () method in the base class. However, this does not mean that this method cannot be used with SUPER.M1 () method calls. This code is not appropriate for a variable that is related to the DERIVED2 reference. Other operational mappings for DERIVED2 also indicate code execution for each type of operation.
Now that you have a Derived2 object, you can refer to it with any variable of the DERIVED2 type. As shown in 1, Derived, base, and Itype are all base classes of Derived2. Therefore, a reference to the base class is useful. Figure 3 depicts the conceptual perspective of the following statement.
Base base = Derived2;
Although the reference to the base class does not have to access M3 () and M4 (), it does not change any of its Derived2 object's characteristics and operation mappings. The code executed by the call to M1 () or M2 (String) is the same whether it is a variable derived2 or base.
Two references invoke the same behavior because the Derived2 object does not know which method to invoke. The object only knows when to invoke it, and it executes in the order of the inheritance implementation. This sequence determines that the Derived2 object calls the M1 () method in derived and calls the M2 (String) method in Derived2. This result depends on the type of the object itself, not the type of the reference.
However, it does not mean that the effect you use DERIVED2 and base references is exactly the same. As shown in 3, the base reference can only see operations owned by the base type. Therefore, although Derived2 has mappings to methods M3 () and M4 (), the variable base cannot access these methods.
The run-time Derived2 object retains the ability to accept the M3 () and M4 () methods. Restrictions on the type of the base make it impossible for a reference to call these methods at compile time. The Compile-time type check is like a set of armor, which guarantees that the run-time object can only interact with the correct operation. In other words, a type defines the bounds of interaction between objects.
Polymorphism of dependency
The consistency of a type is the core of polymorphism. Every reference on an object, the static type checker confirms that such dependency is consistent with the hierarchy of its objects. When a reference succeeds in attaching to a different object, the interesting polymorphic phenomenon arises. (Strictly speaking, the object type refers to the definition of a class.) You can also attach a few different references to the same object. Before starting a more interesting scenario, let's take a look at why the following situation does not produce polymorphism.
Multiple references attached to an object
Figure 2 and Figure 3 describe an example of attaching two and more than two references to an object. Although the Derived2 object retains the type of the variable after it has been attached, the reference to the base type in Figure 3 is dependent upon it, and its functionality is reduced. The conclusion is clear: attaching a reference to a base class to an object of a derived class reduces its ability.
A development how does this choose to reduce the object ability of the scheme? This option is indirect. Suppose you have a reference named ref attached to an object that contains a class of the following methods:
Calling poly (Base) with a Derived2 parameter is in accordance with the parameter type check:
The method call attaches a variable of the local base type to an imported object. So, although this method only accepts parameters of the base type, the Derived2 object is still allowed. Development this eliminates the need to choose a scenario that loses functionality. The dependency of the Base type refers to the loss of functionality from what the human eye sees when it passes through the Derived2 object. However, from the point of view of execution, each incoming poly1 (base) parameter is considered to be a base object. The execution machine does not care that there are multiple references to the same object, it only focuses on passing references to another object to the method. The type inconsistency of these objects is not a major problem. The executor only cares about finding the appropriate implementation for the runtime object. The type-oriented perspective demonstrates the immense power of polymorphism.
References that are attached to multiple objects
Let's look at the polymorphic behavior that occurs in Poly1 (Base). The following code creates three objects and passes a reference to Poly1 (Base):
The implementation code for POLY1 (Base) is the M1 () method that invokes the arguments passed in. Figure 3 and Figure 4 show the architecture used by the type-oriented object when passing three classes of objects to a method.
Note the mapping of the Method M1 () in each diagram. In Figure 3, M1 () invokes the code of the Derived class, and the comments in the code above indicate ploy1 (Base) call DERIVED.M1 (). The derived object in Figure 4 is still called the M1 () method of the derived class. Finally, in Figure 4, the M1 () called by the base object is the code defined in the base class.
What is the charm of polymorphism? Take a look at the code for Poly1 (base), which can accept any parameter that belongs to the base category. However, when he receives a Derived2 object, it actually calls the derived version of the method. When you derive other classes from the base class, such as Derived,derived2,poly1 (base), you can accept these parameters and make a choice to invoke the appropriate method. Polymorphism allows you to extend the use of poly1 (Base) after it has been completed.
It certainly looks magical. The basic understanding demonstrates the internal workings of polymorphism. In a type-oriented perspective, the code implemented by the underlying object is non-substantive. It is important that the type checker select the appropriate code for each reference during compilation to implement its methods. Polymorphism allows developers to use a type-oriented perspective, regardless of implementation details. This helps to separate the type from the implementation (the actual use is to separate the interface from the implementation).
Object interface
Polymorphism relies on the separation of types and implementations, and is used to separate interfaces and implementations. But the following ideas seem to confuse the Java keyword interface.
More important is how developers understand the phrase "the interface to an object", typically, depending on the context, the phrase means all the methods defined in the object class to the method that all objects expose. This tendency towards an implementation-centric perspective makes us more focused on the object's ability to run in the runtime than a type-oriented perspective. In Figure 3, the object surface of the reference panel is marked as "Derived2 object". All available methods of the Derived2 object are listed on this panel. But to understand polymorphism, we have to liberate it from the implementation level and note the panel labeled "Base Reference" in a type-oriented perspective. In this layer of meaning, the type of the reference variable indicates the surface of an object. It's just a surface, not an interface. Under the principle of type consistency, we can use a type-oriented perspective to attach multiple references to an object. There is no definite understanding of the phrase interface to an object.
In the type concept, the interface to an object refers references the largest possible----2 scenario for a type-oriented perspective. To point a reference to a base class to the same object shrinks the view----3. The concept of type can make people get the gist of separating the interaction between objects and realizing the details. A type-oriented approach encourages people to use a reference to an object, relative to an object's interface. The reference type specifies the interaction between objects. When you think about what an object can do, just understand his type, without having to think about the details of his implementation.
Java interface
The polymorphic behavior mentioned above uses the subtype relationship established by the inheritance relationship of the class. Java interfaces also support user-defined types, and, in contrast, Java's interface mechanism initiates polymorphic behavior built on the type hierarchy. Assume a reference variable named ref and point it to a class object that contains a method:
To understand polymorphism in Poly2 (IType), the following code creates two objects from different classes and passes them to Poly2 (IType), respectively:
The above code is similar to the discussion of polymorphism in Poly1 (Base). The implementation code for POLY2 (IType) is the M3 () method that invokes the local version of each object. As before, the code's comments indicate the result of the CString type returned by each call. Figure 5 shows the conceptual structure of two calls to Poly2 (IType):
The similarity of the polymorphic behavior shown in the method Poly1 (Base) and Poly2 (IType) can be seen directly from the Perspective view. We can see the technique of these two pieces of code by raising the level of our understanding on the first level. A reference to a base class points to a class that is passed in as a parameter, and invokes the method of the object by the restriction of the type. The reference neither knows nor cares about which piece of code to execute. The sub-type relationship checks during compilation ensure that the passed object has the ability to select the appropriate implementation code when it is called.
However, they have an important difference in the implementation layer. In the Poly1 (Base) example (Figure 3 and Figure 4), the class inheritance structure of BASE-DERIVED-DERIVED2 provides the conditions for establishing a subtype relationship and determines which code the method calls. In the case of Poly2 (IType) (5), it is a completely different dynamic occurrence. Derived2 and separate do not share any level of implementation, but they also demonstrate polymorphic behavior through Itype references.
Such polymorphic behavior makes the functionality of the Java interface significantly more significant. The UML class diagram in Figure 1 illustrates that derived is a subtype of base and Itype. By completely departing from the definition of the type of implementation details, Java implements multiple types of inheritance, and there is no annoying problem with multiple inheritance that is forbidden by Java. Classes that are completely detached from the implementation hierarchy can be grouped by Java interface implementations. In Figure 1, interfaces Itype and Derived,separate, as well as other subtypes of this type, should be classified as a group.
According to this completely different classification method, Java interface mechanism is polymorphic become very convenient, even if there is no shared implementation or replication method. As shown in 5, a itype reference to the M3 () method of the Derived2 and separate objects is accessed using polymorphic methods.
Explore the interface of an object again
Note the mapping of the Derived2 and separate objects in Figure 5 to the M1 () method. As mentioned earlier, each object's interface contains method M1 (). But there is no way to use these two objects to make the method M1 () Exhibit polymorphic behavior. It is not enough for each object to occupy a M1 () method. There must be a type that can manipulate the M1 () method, through which the object can be seen. These objects seem to share the M1 () method, but polymorphism is not possible without a common base class. This concept can be mixed up by looking at polymorphism through the interface of the object.
Conclusion
You can clearly recognize this type-oriented view from the polymorphism of the subtypes that are created by the object-oriented polymorphism described in this article. If you want to understand the thought of subtype polymorphism, you should shift your attention from the details of implementation to the type. Types divide objects into groups and manage the interfaces of those objects. The type's inheritance hierarchy determines the type relationships that are required to implement polymorphism.
Interestingly, the details of the implementation do not affect the hierarchical structure of the subtype polymorphism. The type determines what method the object calls, and the implementation determines how the object executes the method. In other words, the type indicates responsibility, and the implementation is the concrete implementation. After separating the implementation from the type, we seem to see the two parts dancing together, the type determines the name of his partner and dance, and the realization is the designer of the dance action.
What is the mechanism for polymorphism in Java?