Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand Java programming Polymorphism

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the knowledge of "how to understand Java programming Polymorphism". Many people will encounter this dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Catalogue

I. upward transformation

Second, change planes

1. Bind

2. Scalability

3. Defects

Constructor and Polymorphism

1. The calling order of the constructor

2. The behavior of polymorphic methods inside the constructor

Fourth, covariant return type

Fifth, inherit the design

Foreword:

Encapsulation is to merge attributes and behaviors to create a new data type, inheritance is to establish a relationship between data types (is-a), and polymorphism is the application of this relationship in the actual scene.

Polymorphism is to separate what to do from how to do it; among them, what to do refers to which method to call [play instrument], how to do refers to the implementation [using An instrument to use B instrument], and 'separated' means that two things are not determined at the same time.

I. upward transformation

An object can be used either as its own type or as its base type, and this is an upward transformation to treat a reference to an object as a reference to its base type.

Example:

Public enum Note {/ / play musical notes MIDDLE_C, C_SHARP, BalloFlatt;} public class Instrument {/ / musical instrument base class public void play (Note n) {print ("Instrument.play ()");}} public class Wind extends Instrument {/ / Wind is a specific musical instrument / / Redefine interface method: public void play (Note n) {System.out.println ("Wind.play ()" + n) }} public class Music {/ / instrument playing public static void tune (Instrument I) {i.play (Note.MIDDLE_C);} public static void main (String [] args) {Wind flute = new Wind (); tune (flute); / / upward transition}

Benefits:

In the above example, it seems more intuitive to let the tune method accept a Wind reference as its own parameter, but it raises a problem: at this point you need to write a new tune method for each type of Instrument in the system. So it would be better for us to write a simple method that accepts only the base class as a parameter instead of a special derived class.

Example:

Class Stringed extends Instrument {public void play (Note n) {print ("Stringed.play ()" + n);}} class Brass extends Instrument {public void play (Note n) {print ("Brass.play ()" + n);}} public class Music2 {public static void tune (Wind I) {i.play (Note.MIDDLE_C) } public static void tune (Stringed I) {i.play (Note.MIDDLE_C);} public static void tune (Brass I) {i.play (Note.MIDDLE_C);} public static void main (String [] args) {Wind flute = new Wind (); Stringed violin = new Stringed (); Brass frenchHorn = new Brass (); tune (flute) / / No upcasting tune (violin); tune (frenchHorn);}} II. Transfer public static void tune (Instrument I) {/ /. I.play (Note.MIDDLE_C);}

In the above method, it receives an Instrument reference, so in this case, how does the compiler know that the instrument reference refers to a Wind object? -- through late binding

1. Bind

Associating a method call with a method body is called binding.

Binding before the execution of the program is pre-binding. For example, there is only one method call in C language, that is, pre-binding.

Binding at run time based on the type of object is late binding, also known as dynamic binding or runtime binding.

With the exception of the static method and the final method, all methods in Java are late-bound, which means that usually we don't have to decide whether late binding should be done-- it happens automatically.

2. Scalability

Because of the polymorphic mechanism, you can add as many new types to the system as you want without changing the true () method. In a well-designed OOP program, most or all of our methods follow the model of tune () and communicate only with the interface of the base class. We say that such programs are "extensible" because they can inherit new data types from common base classes, thus adding some new functionality. If it is to meet the requirements of the new class, then the method of manipulating the underlying class interface does not need to be changed at all.

For the musical instrument example, suppose we add more methods [what/adjust] to the base class, as well as a series of new classes [Woodwind/Brass]

Example:

Class Instrument {void play (Note n) {print ("Instrument.play ()" + n);} String what () {return "Instrument";} void adjust () {print ("Adjusting Instrument");}} class Wind extends Instrument {void play (Note n) {print ("Wind.play ()" + n);} String what () {return "Wind";} void adjust () {print ("Adjusting Wind") } class Percussion extends Instrument {void play (Note n) {print ("Percussion.play ()" + n);} String what () {return "Percussion";} void adjust () {print ("Adjusting Percussion");}} class Stringed extends Instrument {void play (Note n) {print ("Stringed.play ()" + n);} String what () {return "Stringed";} void adjust () {print ("Adjusting Stringed") } class Brass extends Wind {void play (Note n) {print ("Brass.play ()" + n);} void adjust () {print ("Adjusting Brass");}} class Woodwind extends Wind {void play (Note n) {print ("Woodwind.play ()" + n);} String what () {return "Woodwind" }} public class Music3 {/ / Doesn't care about type, so new types / / added to the system still work right: public static void tune (Instrument I) {/ /... I.play (Note.MIDDLE_C);} public static void tuneAll (Instrument [] e) {for (Instrument I: e) tune (I) } public static void main (String [] args) {/ / Upcasting during addition to the array: Instrument [] orchestra = {new Wind (), new Percussion (), new Stringed (), new Brass (), new Woodwind ()}; tuneAll (orchestra);}}

Add more types to the instrument system without changing the tune method. The tune method can completely ignore all the changes that have taken place in the code around it and still work normally.

3. Defects

Proprietary method

The private method is automatically modified to final and is shielded from the derived class, so the f method in the subclass Derived class is a brand new method. Since the f method in the base class is not visible in the subclass Derived, it cannot be overloaded.

Domain and static method

Access to any field is parsed by the compiler and is therefore not polymorphic.

If a method is static, it is not polymorphic

Constructor and Polymorphism

In general, constructors are different from other methods, and the same is true when it comes to polymorphism. Constructors are not polymorphic.

1. The calling order of the constructor

The constructor of the base class is always called during the construction of the exported class and is gradually linked up according to the inheritance hierarchy. So that the constructor of each base class can be called.

2. The behavior of polymorphic methods inside the constructor

The hierarchy of constructor calls raises a question: what happens if a dynamically bound method of the object being constructed is called within a constructor?

Public class Glyph {void draw () {print ("Glyph.draw ()");} Glyph () {print ("Glyph () before draw ()"); draw (); / / call a dynamic binding method of the object being constructed, and the object's field radius is initialized to 0 print ("Glyph () after draw ()");}} public class RoundGlyph extends Glyph {private int radius = 1 RoundGlyph (int r) {radius = r; print ("RoundGlyph.RoundGlyph (), radius =" + radius);} void draw () {print ("RoundGlyph.draw (), radius =" + radius);}} public class PolyConstructors {public static void main (String [] args) {new RoundGlyph (5) }} / * Output: Glyph () before draw () RoundGlyph.draw (), radius = 0 Glyph () after draw () RoundGlyph.RoundGlyph (), radius = 5 million Greater Greater

In the constructor of Glyph, we called the draw method. Because this is a dynamically bound method, we will call the draw method in the export class RoundGlyph, but the member radius manipulated by this method has not been initialized, so it shows the problem. The first output radius in the result is 0.

So the actual process of initialization is:

Initialize the storage space allocated to the object to binary zeros before anything else

2 call the base class constructor as mentioned earlier

3 call the initialization method of the member in the declared order

4 call the constructor body of the derived class

Fourth, covariant return type

In object-oriented programming, the covariant return type means that the return value type of the member function in the subclass does not have to be strictly equal to the return value type of the overridden member function in the parent class, but can be a more "narrow" type.

Java 5.0adds support for covariant return types, that is, when subclasses override (that is, override) base class methods, the returned type can be a subclass of the base class method return type. Covariant return types allow more specific types to be returned.

Example:

Import java.io.ByteArrayInputStream;import java.io.InputStream;class Base {/ / subclass Derive will override this method to return the subclass public InputStream getInput () {return System.in;}} public class Derive extends Base {@ Override public ByteArrayInputStream getInput () {return new ByteArrayInputStream (new byte [1024]);} public static void main (String [] args) {Derive d=new Derive () System.out.println (d.getInput (). GetClass ());}} / * Program output: class java.io.ByteArrayInputStream*/ 5. Inheriting and designing class Actor {public void act () {}} class HappyActor extends Actor {public void act () {System.out.println ("HappyActor");}} class SadActor extends Actor {public void act () {System.out.println ("SadActor");}} class Stage {private Actor actor = new HappyActor () Public void change () {actor = new SadActor ();} public void performPlay () {actor.act ();}} public class Transmogrify {public static void main (String [] args) {Stage stage = new Stage (); stage.performPlay (); stage.change (); stage.performPlay ();}}

Output:

HappyActor

SadActor

A general rule is: "inheritance is used to express differences between behaviors, and fields are used to express changes in state." In the above example, both are used: two different classes are inherited to express the differences in the act () method, while Stage changes its state by using combinations. In this case, this change in state leads to a change in behavior.

That's all for "how to understand Java programming Polymorphism". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report