In Dart 3, why can you extend a `final` class from the same file but not from another file in the same package?
answer
- library, not package
- a file plus its parts
- restrictions are for other libraries
- part of shares the library
basics
~20 sClass modifiers restrict other libraries, and in Dart a library is one file plus its part files, not a package. A second file in the same package is a separate library, so it cannot extend or implement a final class.
solid answer
~40 sDart's `base`, `interface`, `final` and `sealed` restrictions apply to code outside the declaring **library**, and dart.dev defines a library as every Dart file plus its parts. Inside `payments.dart` you can freely extend a `final class PaymentMethod` (the subclass must still be `base`, `final` or `sealed`). But `lib/src/wallet.dart` in the same package is a different library, so `class Wallet extends PaymentMethod` there is an error. The dart.dev maintainer guide states it: the restriction applies to other packages and even other libraries within your own package. To spread a sealed or final hierarchy across files, use `part` / `part of`, which keeps them in one library.
code
dart · 23 lines// lib/payments.dart
part 'src/wallet.dart';
sealed class PaymentMethod {
const PaymentMethod();
}
final class Card extends PaymentMethod {
const Card(this.last4);
final String last4;
}
// lib/src/wallet.dart
part of '../payments.dart';
final class Wallet extends PaymentMethod { // OK: same library via part of
const Wallet(this.provider);
final String provider;
}
// lib/src/voucher.dart (a separate library)
// import '../payments.dart';
// final class Voucher extends PaymentMethod {} // error: outside the sealed class's librarygo deeper
Recall that a Dart library is a file plus its parts, and that modifiers restrict other libraries, including other files in your own package.
Explain which rules relax inside the declaring library and which do not, such as base transitivity and abstract construction.
Lay out files for closed hierarchies on purpose, using part files where needed, and plan how tests will fake final or base types.
Decide how library boundaries map to team ownership so modifiers enforce the right seams without forcing awkward part-file sprawl.
## The unit is the library Dart's class modifiers — `base`, `interface`, `final` and `sealed` — restrict what **other libraries** may do with a class. Inside the declaring library, you may extend a `final` or `interface` class and implement a `base` class. So the question "who counts as outside?" decides everything. dart.dev answers it on the libraries page: **every Dart file (plus its parts) is a library**, even without a `library` directive. A package is a collection of libraries; it is **not** a privacy or modifier boundary. ## The consequence developers trip over A team splits a payments feature across files: ```text lib/ payments.dart // final class PaymentMethod src/wallet.dart // class Wallet extends PaymentMethod <- error ``` Even though both files live in the same package and the same team owns them, `src/wallet.dart` is a separate library. The analyzer reports that the class can't be extended outside of its library because it's a final class. The dart.dev maintainer guide makes the point about `interface` explicitly: the restriction applies to other packages, *and even other libraries within your own package*. The same boundary governs: - **`sealed`**: every direct subtype must be in the declaring library, so a sealed hierarchy cannot be scattered across ordinary files; - **`base`**: implementing it from `src/wallet.dart` is an error even inside your own package; - **`interface`**: extending it from another file is an error. ## Keeping a hierarchy in one library There are two ways to keep a closed hierarchy together: 1. **Put it in one file.** Small sealed families (three to five variants) usually fit comfortably. 2. **Use `part` files.** `payments.dart` declares `part 'src/wallet.dart';`, and the other file starts with `part of '../payments.dart';`. Both files are then **one library**, so `Wallet` may extend a `final` or `sealed` `PaymentMethod`. `part` files share the library's namespace and privacy, so treat them as one unit; the details of `part` directives belong to the libraries and packages topic. ## What still applies inside the library Being in the same library does not switch every rule off: | Rule | Other library | Same library | |---|---|---| | Extend a `final` or `interface` class | error | allowed | | Implement a `base` or `final` class | error | allowed | | Declare a direct subtype of a `sealed` class | error | allowed | | Subtype of `base`/`final` must be `base`, `final` or `sealed` | required | **still required** | | Instantiate an `abstract` or `sealed` class itself | error | **still an error** | `abstract` is not an access modifier at all: it forbids instantiating the class itself everywhere, including the declaring library (factory constructors that return a subtype are still allowed). ## Why Dart chose the library Privacy in Dart is already library-scoped: an identifier starting with `_` is visible within its library only. Class modifiers reuse that boundary, which is what makes the `base` guarantee meaningful — only code in the declaring library can see private members, so only that code may implement the class without inheriting them. ## Practical checklist - Before marking an exported class `final` or `sealed`, list where its subclasses live today. - Move subtypes into the declaring file or convert them to `part` files. - Remember that tests in `test/` are other libraries: they cannot extend or implement your `final` classes either, which affects how you write fakes.
- In Dart 3, can a test file in test/ implement your final class to build a fake?No. The test file is a different library, so implementing or extending a `final` class there is a compile error. You either construct real instances, fake at a different seam such as an `interface` type the final class depends on, or reconsider whether `final` is the right modifier for that type.
- In Dart 3, does being in the same library let you construct a sealed class?No. `sealed` implies `abstract`, and `abstract` forbids instantiating the class itself in every library, including the declaring one. The same library gains the right to declare the direct subtypes, and the sealed class may offer factory constructors that return one of them.
saying these in an interview costs you the question
- Class modifiers restrict other packages, so files in my package are exempt.
- A library means a directory or a package in Dart.
- part files are separate libraries with their own modifier boundary.
- Inside the declaring library the base transitivity rule is relaxed too.