When would you use Embedding Basics in a real project
Fast recall
Quick revision
Core answer
Practical understanding of Embedding Basics.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The comparison in terms that expose every difference:
- 1
Type relationship: inheritance makes Admin a User (assignable, substitutable — polymorphism through the hierarchy). Embedding makes Admin CONTAIN a User (a.User is a real field). No substitutability — code taking User cannot take Admin; there is no is-a.
- 2
Method binding: inheritance dispatches VIRTUALLY — child overrides reach the parent's call sites (fragile base: parent calls self.Method(), the child's override hijacks it). Embedding binds STATICALLY — Admin.Greet and User.Greet are separate; an outer shadow doesn't affect the inner's other methods, and calls to promoted methods always reach the embedded type's implementation.
- 3
Overriding: inheritance's override REPLACES. Embedding's outer shadow HIDES — the inner's method continues to exist and run wherever the inner type is used. Admin.Greet shadows; User.Greet is still what a User-typed variable calls, and still what a.User.Greet() gets.
- 4
Initialization and identity: an Admin must be constructed with a User IN it (Admin{User: ..., Level: 9}) — the composition is explicit at every creation site. Inheritance hides the parent's construction in the child's. The explicitness follows through equality, copying, JSON shape (the embedded User's fields FLATTEN into the outer's object — a serialization consequence, not a type-system one).
The design argument an interviewer wants articulated: inheritance bundles four things together — code reuse, subtyping, dynamic dispatch, and polymorphism — into one mechanism with known costs (fragile base, deep hierarchies, accidental coupling). Go splits the bundle: reuse → embedding; polymorphism + dispatch → interfaces. Each mechanism does one thing, and the type system stays decidable: method calls resolve statically except through interfaces, where the dispatch is explicit in the type (interface value). The cost is ceremony — where Java writes 'class Admin extends User implements Greeter,' Go writes an embedded field plus a method-set rule. The benefit is that every line of a Go program's behavior is traceable to a declaration in view.
// embedding: has-a + promotion
type Admin struct { User; Level int }
a.Name // promoted field
a.Greet() // promoted — calls User's method
// inheritance would give: var u User = admin — SUBTYPE
// Go: compile error. Admin is NOT a User. No is-a exists.When would you use Embedding Basics in a real project
Direct Answer
Embedding: has-a with promoted access; the inner stays itself (accessible via the field name); static binding; no subtype. Inheritance: is-a; virtual dispatch; the subtype IS the parent. Go chose composition — embedding — and interfaces for polymorphism, deliberately without the class hierarchy.
Deep Dive & Architecture
The dependency comparison, mechanized: inheritance's override replaces — a child method substitutes for the parent's in ALL contexts, including calls the parent makes on itself (virtual dispatch reaching through the base). Embedding's shadow hides — the inner's method continues to exist and run wherever the inner type is used; only the unqualified outer path is shadowed. A parent calling its own Greet() still gets ITS Greet() in Go's model — there is no dispatch layer for the child's version to hijack — which is precisely the fragile-base-class failure mode Go designed out. The trade is explicitness: where inheritance gives you automatic specialization, Go requires you to write the outer method and route to the inner explicitly when you need both. That cost buys locality — every behavior question ('which Greet runs here?') is answerable from the types in view, never from a hierarchy walk.
The initialization story is the second axis: composition makes the inner explicit at construction (Admin{User: ...} — the Admin IS built from a User, visibly). Inheritance hides the parent's construction inside the child's — convenient until you need to know what was built. Go's literal syntax keeps the has-a honest: the memory layout is two adjacent values (or a pointer), and the promotion is name-resolution sugar over that layout — nothing more. Reviewers can read the embedding as what it physically is: a field with special lookup rules.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Embedding Basics, and when would you use it?
- How would you compare Embedding Basics with an alternative?
- What is a common mistake when using Embedding Basics?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.