Dart (3.13) Module 9
Inheritance, mixins, class modifiers and extensions interview questions
Interview questions on inheritance, mixins, class modifiers and extensions: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
When should you use implements instead of extends in Dart?
Answered in the lesson Inheritance, abstract classes and interfaces
-
Fresher to mid
What is the difference between final class and sealed class in Dart, and when do you choose each?
Both prevent extending and implementing outside the library, but sealed also prevents constructing and makes the class implicitly abstract. Choose final when you want users to use instances but never subtype them, such as a Money value type. Choose sealed when you want the compiler to know every subtype for exhaustive switches, such as a PaymentResult with Success and Failure subtypes in the same library. A final class Money can be constructed; a sealed class PaymentResult cannot, and its subtypes must appear in the same library.
From the lesson Class modifiers: base, interface, final, sealed
-
Fresher
In Dart 3, when must a type be declared with mixin or mixin class rather than class?
-
Fresher to mid
A shop has four order statuses (placed, packed, shipped, delivered) with no data attached. Would you model them as an enum or as a sealed class, and what changes if Cancelled has to carry a refund amount?
An enum fits: the four are plain values, and a switch over an enum must list every constant. Once
Cancelledalone needs a refund amount, an enum constant cannot hold it, because every constant of an enum has the same fields. Move to a sealed class with a final subclass per status;Cancelledgets its ownrefundPaise, a switch binds it withCancelled(:final refundPaise), and the compiler still lists every switch that forgets a status.From the lesson Sealed classes and exhaustive switches
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
A Dart library ships a Logger that most apps extend and override one method of. One team wants a stricter contract: no subclass may skip a method, and no app may call Logger directly. Compare an abstract class, an interface class and an abstract interface class, and say what each costs the library and its users.
- What it tests
- Knowing that an abstract class may still carry concrete bodies, that interface class forbids extends from outside the library, and that abstract interface class gives the strictest shape; and how each choice affects evolution.
- First step
- Write down what Logger has today: a few abstract methods and a few concrete helpers that most subclasses reuse. Then ask what the team's "stricter contract" means in Dart terms: no instantiation (abstract), no reuse of bodies (every method abstract), and no extends from outside (interface class). The combination picks the modifier.
- Follow-ups
-
- Which of the three keeps the shared helper methods available to users?
- What breaks for apps that already extend Logger if the library adds a modifier?
- How does implements still work when extends is forbidden?
From the lesson Inheritance, abstract classes and interfaces
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A support app routes a message to one of three channels at run time. Draft one is a Notifier with an if/else over an enum; draft two an inheritance tree of SmsNotifier, EmailNotifier and WhatsAppNotifier; draft three a Dispatcher holding a Map<Channel, Notifier> that delegates. Which would you ship, and where does each fail first?
- What it tests
- Knowing why a tagged if/else and a wide inheritance tree are both hard to extend, and how composition keeps each channel a short, testable class the dispatcher looks up by key.
- First step
- List the forces: three channels today, more later; each channel's send has its own quirks; tests must not call a real gateway. Then ask which design lets a new channel ship without editing a shared method, and which lets a test swap in a fake with a one-line change.
- Follow-ups
-
- What goes wrong first when the if/else version adds a fourth channel?
- How does a wide inheritance tree hide which class a fix should live in?
- Where does the composed Dispatcher put the opt-in rule that chooses the channel?
From the lesson Inheritance, abstract classes and interfaces
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A payments library published version 1.0.0 with an open Gateway class. Some users extend it, others implement it. You want to add interface to Gateway in the next release. Plan the migration so that users have time to adapt, and explain which code breaks and when.
- What it tests
- Knowing that adding a modifier is a breaking change, how @Deprecated.extend() warns users before the break, and what semantic versioning says about the version numbers.
- First step
- List who breaks: users who extend Gateway will see compile errors when interface is added. Users who only implement it are fine because interface allows implementing. Start by deprecating the extending.
- Follow-ups
-
- Which version should add @Deprecated.extend()?
- Which version should add the interface modifier?
- How do users who extend Gateway migrate their code?
From the lesson Class modifiers: base, interface, final, sealed
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to staff
Your library exports base class Ledger. A user's code compiles today with class AppLedger extends Ledger. In a new release you add a private method _auditEntry() that record() calls. Does the user's code still compile, and if not, what do they see?
- What it tests
- Knowing that base transitivity requires subtypes to be base, final, or sealed, how the compiler enforces this, and what happens to existing code that extends a base class without the marker.
- First step
- Recall what base transitivity enforces: any subtype of a base class must itself be marked base, final, or sealed. Check whether the user's class AppLedger has such a marker.
- Follow-ups
-
- Why does base transitivity exist as a rule?
- What modifier should the user add to AppLedger, and why?
- How does this protect the private _auditEntry() method?
From the lesson Class modifiers: base, interface, final, sealed
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
You maintain a Dart package with a class that users extend, implement, and mix in. You want to restrict it to implementing only. Describe the deprecation and release sequence, naming the exact annotation constructors available since Dart 3.10 and what each one warns about.
- What it tests
- Knowing the @Deprecated constructors for subtyping (extend, implement, mixin, subclass), what each warns about, and how to combine them with semantic versioning for a multi-release migration.
- First step
- List the subtype relationships you need to forbid: extending and mixing in, but not implementing. Then map each to the @Deprecated constructor that warns about it.
- Follow-ups
-
- Which @Deprecated constructor warns about extending?
- Which @Deprecated constructor warns about mixing in?
- What modifier on the class forbids both extending and mixing in but allows implementing?
From the lesson Class modifiers: base, interface, final, sealed
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Four services and three repositories need the same log, metrics and retry behaviour. Draft one is a base class LoggedRetrying the seven extend; draft two is three mixins the seven list in with; draft three is a Pipeline that wraps every call. Which would you ship, and where does each fail first?
- What it tests
- Knowing when "has-a" beats "is-a", when a mixin layered left to right beats a parent, and how composition through a wrapper differs from both at the API that callers see.
- First step
- Write down the shared behaviours and ask whether the services and repositories are of one kind. They are not; the base class forces them under one parent for behaviours none of them owns. Then ask whether a new behaviour should be opt-in by host (a mixin), applied to every host at once (a parent) or wrap each call from outside (composition).
- Follow-ups
-
- Where does the base class design fail first when the cache layer is added?
- What does the mixin version give that a Pipeline wrapper does not?
- When does a Pipeline wrapper beat a mixin, and what does it cost the caller?
From the lesson Mixins and the mixin class
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Senior to staff
A payments service uses class Pay extends GatewayBase with Log, Metrics, Retry. A new "verify after retry" check runs twice for every retry, and the team suspects the super chain. Walk through how you would read the order, the measurement you would take to confirm it, and the one-token fix if the chain is wrong.
- What it tests
- Knowing that the with list reads left to right but applies bottom to top, that super from each mixin goes to the next slot below in the linearization, and how a measurement proves which body runs between two sends.
- First step
- Read the with list: Log, Metrics, Retry puts Retry on top, with Metrics below it and Log at the bottom just above the base. So a call on Pay().charge(...) starts at Retry.send, which calls super.send into Metrics, which calls super.send into Log, which calls super.send into GatewayBase. The team's hunch, "verify runs twice", fits Retry over Metrics: Metrics.send runs on every retry, so a counter inside it doubles when Retry loops.
- Follow-ups
-
- What one program prints enough to decide whether the chain is really wrong?
- If the chain is wrong, which two mixins swap in the with list?
- When is it better to move the behaviour out of a mixin into a method on the host?
From the lesson Mixins and the mixin class
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Mid to senior
The team asks why mixin Metrics on Notifier cannot take a MetricsClient in its constructor. Explain what Dart forbids there and show two patterns that keep every host independent: the mixin's own abstract getter, and a static Metrics.of lookup. Say which you would ship and why.
- What it tests
- Knowing that a mixin may declare no generative constructor, how an abstract getter lets the host supply the state it needs, and when a service locator inside the mixin beats a getter on every host.
- First step
- Dart's rule is one line: a mixin or mixin class may not declare a generative constructor (dart.dev/language/mixins). no_ctor.dart in the lesson shows the exact error, "Mixins can't declare constructors." The design question is then where the MetricsClient should come from, and the two usual answers are the host (through an abstract getter) and the environment (through a static lookup on the mixin or its own package).
- Follow-ups
-
- What does the abstract getter cost the host, and when is that cheap?
- How does a static Metrics.of lookup hide the dependency, and when is that a problem?
- Which design still works when a test needs a fake MetricsClient for one test only?
From the lesson Mixins and the mixin class
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Mid to senior
A pipeline stores rows as List<dynamic> and calls rows.first.toUpperCase() on each batch. Upstream starts sending ints alongside strings, and the crash says NoSuchMethodError. A colleague suggests an extension on Object with toUpperCase. Explain why that fix cannot work here, what the real cause is, and how you would redesign the pipeline to fail earlier.
- What it tests
- Knowing that extensions are resolved by static type, not runtime type, why an extension on Object still does not apply to a variable typed dynamic, and how moving the type boundary earlier surfaces type errors at compile time.
- First step
- Start with what the compiler sees: rows.first has static type dynamic, and extensions are picked at compile time by static type. No extension applies to dynamic, no matter what type it extends. The call becomes a dynamic dispatch that fails when the object has no toUpperCase.
- Follow-ups
-
- Why does an extension on Object not apply to a variable typed dynamic?
- How would you change the pipeline to catch the int before calling a string method?
- What is the difference between extension resolution and regular polymorphism?
From the lesson Extension methods in Dart
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A Flutter app imports two packages that each define extension ParseHelpers on String with a toDateTime(). One parses ISO 8601, the other parses dd-MM-yyyy. Different screens need different formats. How do you use both extensions without renaming either package, and how do you organise imports so each screen gets the right parser?
- What it tests
- Knowing that two extensions with the same name and same member cause an ambiguity error, how explicit application with ExtensionName(value).method() selects one extension, how import prefixes qualify extension names, and how file-level import strategies keep each screen unambiguous.
- First step
- Identify the conflict: both libraries export ParseHelpers with toDateTime(). Importing both in one file makes '2025-01-15'.toDateTime() ambiguous. Resolve it file by file: each screen imports only what it needs, or uses explicit application when both are needed.
- Follow-ups
-
- What are the four ways to resolve an extension conflict at the import site?
- How does explicit application differ from a static method call?
- If you control one of the packages, what naming change prevents future conflicts?
From the lesson Extension methods in Dart
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
You are designing a utility library for an e-commerce team. Some helpers format prices, others validate product codes. Should these be extensions or static utility classes, when should an extension be named versus unnamed, and what type should each extension extend? Walk through the trade-offs and give your recommendation.
- What it tests
- Knowing that extensions read left-to-right while static calls read inside-out, that named extensions can be imported and applied explicitly, that unnamed extensions stay private, and that narrower types reduce conflict risk and improve discoverability.
- First step
- List the helpers and their natural receivers: a price formatter works on num or int, a product code validator works on String. Extensions let callers write price.toRupees() instead of PriceFormat.toRupees(price), which chains naturally and shows up in autocomplete.
- Follow-ups
-
- When would you choose a static method over an extension?
- How does the choice of extended type affect conflict risk and discoverability?
- What naming convention helps other teams avoid clashing with your extensions?
From the lesson Extension methods in Dart
Model answer, Pro.In the answer:
- Model answer
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Your package shop_payments 2.3.0 exports a sealed class Payment with Upi and Card, and apps across the company switch on it with no wildcard. You must add NetBanking. Explain what breaks for those apps, which version you ship, and when you would have chosen a final class with a documented default instead.
- What it tests
- Knowing that a new subtype of a sealed class is a breaking change for exhaustive switches, how semantic versioning treats it, what happens to switches that end in a wildcard, and the sealed against final trade-off.
- First step
- List who is affected before choosing anything: a switch over Payment with no wildcard stops compiling after the upgrade, and a switch with a wildcard keeps compiling but gives NetBanking its default. Then read the version constraints your consumers use, because they decide how loud the release must be.
- Follow-ups
-
- Which version number do you ship, and what does the changelog tell consumers to do?
- What changes for an app whose switches end in a wildcard?
- When is a final class with a default case the better design?
From the lesson Sealed classes and exhaustive switches
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A returns team tracks a return request through Requested, Accepted, PickedUp, Refunded and Declined, with events Accept, Decline, Pickup and Refund, and Refund carries an amount in paise. Design the types and the transition function, say what an illegal event returns, and decide where a wildcard is acceptable.
- What it tests
- Modelling states, events and refusals as sealed families, a transition that returns the next state or a typed error, testing every pair of state and event, and a deliberate choice about where wildcards are allowed.
- First step
- Write the legal moves as a table before any code: for each state, which events are allowed and what each one produces. Count the pairs and the legal ones, because the table becomes both the cases of the switch and the inputs of the test.
- Follow-ups
-
- What does an illegal event return, and who has to handle it?
- How do you test every pair without writing each one by hand?
- Where is a wildcard acceptable in this machine, and where is it not?
From the lesson Sealed classes and exhaustive switches
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to staff
A 200-file Dart app has about 60 switches over a sealed Payment, and 35 of them end in a wildcard. Last month a new subtype went live and one wildcard silently priced it wrong. Plan a sweep that finds the wildcards hiding behaviour, decide which to keep, and say how you stop new ones from arriving unnoticed.
- What it tests
- Knowing that a wildcard hides new subtypes, how to review many wildcards in bulk without changing behaviour, which ones are worth keeping, and what keeps new ones from arriving unnoticed.
- First step
- Count first: search for wildcard cases and group the hits by the type being switched. Then order the hits on sealed types by what a wrong default would cost, since that order is the order of the sweep.
- Follow-ups
-
- How do you decide which wildcards stay?
- What does the analyzer say about a wildcard that covers nothing?
- How do you stop new wildcards over sealed types from arriving unnoticed?
From the lesson Sealed classes and exhaustive switches
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes