This is a creation in Article, where the information may have evolved or changed.
Embedding
Go does not provide the typical, type-driven notion of subclassing, but it does has the ability to "borrow" pieces of a Implementation by embedding types within a struct or interface.
Go does not have the concept of class inheritance in other object-oriented languages but it can use the functionality of the embedded type by embedding other types in the struct or interface
Interface embedding is very simple. We ' ve mentioned the IO. Reader and IO. Writer interfaces before; Here is their definitions.
The embedded interface is very simple. We mentioned IO before. The reader and Io.writer interfaces are defined as follows:
Type Reader Interface { Read (p []byte) (n int, err error)}type Writer interface { Write (p []byte) (n int, err Erro T);
The IO package also exports several and other interfaces this specify objects that can implement several such methods. For instance, there is IO. Readwriter, an interface containing both Read and Write. We could specify IO. Readwriter by listing the other methods explicitly, but it's easier and more evocative to embed the both interfaces to form T He new one, like this:
The IO package exports other interfaces for example IO. Readwriter This interface contains read and write of course we can define the IO directly. The Readwriter interface defines the corresponding write and read functions directly, but embedding the interface is a better way to take advantage of what you already have.
Readwriter is the interface that combines the reader and Writer Interfaces.type Readwriter interface { reader W Riter}
This says just what it looks like:a Readwriter can does what a Reader does and what a Writer does; It is a union of the embedded interfaces (which must be disjoint sets of methods). Only interfaces can is embedded within interfaces.
Readwriter also provides reader and writer functionality only interfaces can be embedded in the interface
The same basic idea applies to structs, but with more far-reaching implications. The BUFIO package has both struct types, Bufio. Reader and Bufio. Writer, each of which of course implements the analogous interfaces from package IO. And Bufio also implements a buffered reader/writer, which it does by combining a reader and a writer into one struct using Embedding:it lists the types within the struct but does not give them field names.
Structs can be embedded in other types of bufio packages with two struct type Bufio. Reader and Bufio. Writer they implement the interface of the same function as reader writer in IO packet Bufio by embedding reader and writer into the same structure, the structure lists only the embedded types but does not give the corresponding struct field names:
Readwriter stores pointers to a Reader and a writer.//It implements IO. Readwriter.type readwriter struct { *reader //*bufio. Reader *writer //*bufio. Writer}
The embedded elements is pointers to structs and of course must is initialized to point to valid structs before they can be used. The Readwriter struct could be written as
An embedded element is a pointer to a struct that must be initialized before it is used readwriter struct can also be written as:
Type readwriter struct { reader *reader writer *writer}
Promote the methods of the fields and to satisfy the IO interfaces, we would also need to provide forwarding M Ethods, like this:
However, if you use this method to give the field name, you need to do a conversion at the time of implementation to pass the corresponding operation to Readwrtier.reader or Readwriter.writer
Func (rw *readwriter) Read (P []byte) (n int, err error) { return Rw.reader.Read (p)}
By embedding the structs directly, we avoid this bookkeeping. The methods of embedded types come along for free and which means that bufio. Readwriter not only have the methods of Bufio. Reader and Bufio. Writer, it also satisfies all three interfaces:io. Reader, Io. Writer, and Io. Readwriter.
By embedding the struct directly (without the field name), it is possible to avoid having to do a conversion of the embedded type once the method is implemented, which means that the BUFIO is used directly. Readwriter not only possessed the Bufio.reader Bufio. Writer's method It also satisfies the IO. Reader io. Writer io. Readwriter interface
There's an important on which embedding differs from subclassing. When we embed a type, the methods of this type become methods of the outer type, but when they is invoked the receiver of The method is the inner type and not the outer one. In we example, when the Read method of a Bufio. Readwriter is invoked, it had exactly the same effect as the forwarding method written out above; The receiver is the reader field of the Readwriter, not the readwriter itself.
An important difference between embedding and class inheritance is that we can embed the type after the type method becomes our method but when these methods are invoked the recipient of the method is the embedded type instead of our own type in the above example when Bufio. When the Readwriter read method is called, it produces the same effect as the field name we defined previously (reader *reader), passing the call to the inner type.
Embedding can also is a simple convenience. This example shows an embedded field alongside a regular, named field.
Type embedding can actually be very simple to see the following code job has a directly embedded type *log. Logger and the type Command string with the field name:
Type Job struct { Command string *log. Logger}
The Job type now have the Log, Logf and other methods of *log. Logger. We could has given the Logger a field name, of course, but it's not necessary to do so. And now, once initialized, we can log to the Job:
The job now has *log. Logger method such as log LOGF now when it is initialized we can use log directly through the job:
Job. Log ("Starting now ...")
The Logger is a regular field of the struct and we can initialize it in the usual to with a constructor,
Logger is a field in a struct we can use the normal initialization method:
Func newjob (command string, logger *log. Logger) *job { return &job{command, Logger}}
Or with a composite literal, or initialized with a literal value:
Job: = &job{command, log. New (OS. Stderr, "Job:", log. Ldate)}
If We need to refer to an embedded field directly, the type name of the field, ignoring the package qualifier, serves as a Field name. If we needed to access the *log. Loggerof a job variable job, we would write job. Logger. This would is useful if we wanted to refine the methods of Logger.
If we need to explicitly refer to the embedded field can take advantage of the field type without the package prefix to achieve the purpose see this code we want to access the job variable job *log. Loggerof field we can write a job. Logger is useful in the way that you need to redefine Logger
Func (Job *job) LOGF (format string, args ... interface{}) { job. Logger.logf ("%q:%s", Job.command, FMT. Sprintf (format, args ...)}
Embedding types introduces the problem of name conflicts but the rules to resolve them is simple. First, a field or method x hides any and item x in a more deeply nested part of the type. If log. Logger contained a field or method called command, the command field of Job would dominate it.
Type embedding causes problems with naming conflicts but the solution is simple the first field or method x hides the other x nested in the deep, if log. Logger contains a command field or method then the job's outermost command hides the command in Log.logger.
Second, if the same name appears at the same nesting level, it's usually an error; It would is erroneous to embed log. Logger if the Job struct contained another field or method called Logger. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; There is no problem if a field was added that conflicts with another field in another subtype if neither field is ever used .
Second, if the same name appears in the same nested hierarchy, it usually results in an error if the job already has another field or if the method is logger then nest the log. Logger may be the wrong thing to do, but if the same name only appears in the type definition except for the type definition, it's not referenced anywhere else. This restriction can protect the type of changes that are embedded elsewhere if a field is added and conflicts with fields in other subtypes But neither of these conflicting fields is used so everything is OK