Pular para o conteúdo principal

Modificadores de classe

Palavras-chave modificadoras para declarações de classe para controlar o acesso de bibliotecas externas.

Modificadores de classe controlam como uma classe ou mixin pode ser usado, tanto dentro de sua própria biblioteca, quanto fora da biblioteca onde está definido.

Palavras-chave modificadoras vêm antes de uma declaração de classe ou mixin. Por exemplo, escrever abstract class define uma classe abstract. O conjunto completo de modificadores que podem aparecer antes de uma declaração de classe incluem:

  • abstract
  • base
  • final
  • interface
  • sealed
  • mixin

Apenas o modificador base pode aparecer antes de uma declaração de mixin. Os modificadores não se aplicam a outras declarações como enum, typedef, extension ou extension type.

Ao decidir se deve usar modificadores de classe, considere os usos pretendidos da classe e quais comportamentos a classe precisa poder confiar.

Sem modificador

#

Para permitir permissão irrestrita para construir ou subtipificar de qualquer biblioteca, use uma declaração class ou mixin sem um modificador. Por padrão, você pode:

abstract

#

Para definir uma classe que não requer uma implementação completa e concreta de toda a sua interface, use o modificador abstract.

Classes abstract não podem ser construídas de nenhuma biblioteca, seja a própria ou uma biblioteca externa. Classes abstract frequentemente têm métodos abstract.

a.dart
dart
abstract class Vehicle {
  void moveForward(int meters);
}
b.dart
dart
import 'a.dart';
​
// Error: `Vehicle` can't be instantiated because
// it is marked as `abstract`.
Vehicle myVehicle = Vehicle();
​
// Can be extended.
class Car extends Vehicle {
  int passengers = 4;
​
  @override
  void moveForward(int meters) {
    // ...
  }
}
​
// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

Se você quiser que sua classe abstract pareça ser instanciável, defina um factory constructor.

base

#

Para forçar a herança da implementação de uma classe ou mixin, use o modificador base. Uma classe base não permite implementação fora de sua própria biblioteca. Isso garante:

  • O construtor da classe base é chamado sempre que uma instância de um subtipo da classe é criada.
  • Todos os membros privados implementados existem nos subtipos.
  • Um novo membro implementado em uma classe base não quebra subtipos, já que todos os subtipos herdam o novo membro.
    • Isso é verdade a menos que o subtipo já declare um membro com o mesmo nome e uma assinatura incompatível.

Você deve marcar qualquer classe que implementa ou estende uma classe base como base, final ou sealed. Isso impede que bibliotecas externas quebrem as garantias da classe base.

a.dart
dart
base class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';
​
// Can be constructed.
Vehicle myVehicle = Vehicle();
​
// Can be extended.
base class Car extends Vehicle {
  int passengers = 4;
  // ...
}
​
// ERROR: `Vehicle` can't be implemented in a different library because
// it is marked with `base`.
base class MockVehicle implements Vehicle {
  @override
  void moveForward() {
    // ...
  }
}

interface

#

Para definir uma interface, use o modificador interface. Bibliotecas fora da própria biblioteca que define a interface podem implementar a interface, mas não estendê-la. Isso garante:

  • Quando um dos métodos de instância da classe chama outro método de instância em this, ele sempre invocará uma implementação conhecida do método da mesma biblioteca.
  • Outras bibliotecas não podem sobrescrever métodos que os próprios métodos da classe de interface podem chamar posteriormente de maneiras inesperadas. Isso reduz o problema da classe base frágil.
a.dart
dart
interface class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';
​
// Can be constructed.
Vehicle myVehicle = Vehicle();
​
// ERROR: `Vehicle` can't be extended in a different library because
// it is marked with `interface`.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}
​
// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

abstract interface

#

O uso mais comum para o modificador interface é definir uma interface pura. Combine os modificadores interface e abstract para uma abstract interface class.

Como uma classe interface, outras bibliotecas podem implementar, mas não podem herdar, uma interface pura. Como uma classe abstract, uma interface pura pode ter membros abstract.

a.dart
dart
abstract interface class Vehicle {
  void moveForward(int meters);
}
b.dart
dart
import 'a.dart';
​
// ERROR: `Vehicle` can't be instantiated because
// it is marked as `abstract`.
Vehicle myVehicle = Vehicle();
​
// ERROR: `Vehicle` can't be extended in a different library because
// it is marked with `interface`.
class Car extends Vehicle {
  int passengers = 4;
​
  @override
  void moveForward(int meters) {
    // ...
  }
}
​
// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

final

#

Para fechar a hierarquia de tipos, use o modificador final. Isso impede a subtipificação de uma classe fora da biblioteca atual. Desabilitar tanto a herança quanto a implementação impede completamente a subtipificação. Isso garante:

  • Você pode adicionar com segurança mudanças incrementais à API.
  • Você pode chamar métodos de instância sabendo que eles não foram sobrescritos em uma subclasse de terceiros.

Classes final podem ser estendidas ou implementadas dentro da mesma biblioteca. O modificador final engloba os efeitos de base, e portanto quaisquer subclasses também devem ser marcadas como base, final ou sealed.

a.dart
dart
final class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}
b.dart
dart
import 'a.dart';
​
// Can be constructed.
Vehicle myVehicle = Vehicle();
​
// ERROR: `Vehicle` can't be extended in a different library
// because it is marked `final`.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}
​
// ERROR: `Vehicle` can't be implemented in a different library because
// it is marked `final`.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

sealed

#

Para criar um conjunto conhecido e enumerável de subtipos, use o modificador sealed. Isso permite que você crie um switch sobre esses subtipos que é estaticamente garantido ser exaustivo.

O modificador sealed impede que uma classe seja estendida ou implementada fora de sua própria biblioteca. Classes sealed são implicitamente abstract.

  • Elas não podem ser construídas por si mesmas.
  • Elas podem ter factory constructors.
  • Elas podem definir construtores para suas subclasses usarem.

Subclasses de classes sealed, no entanto, não são implicitamente abstract.

O compilador está ciente de quaisquer possíveis subtipos diretos porque eles só podem existir na mesma biblioteca. Isso permite que o compilador alerte você quando um switch não lida exaustivamente com todos os possíveis subtipos em seus cases:

dart
sealed class Vehicle {}
​
class Car extends Vehicle {}
​
class Truck implements Vehicle {}
​
class Bicycle extends Vehicle {}
​
// ERROR: `Vehicle` can't be instantiated because
// it is marked `sealed` and therefore, implicitly abstract.
Vehicle myVehicle = Vehicle();
​
// Subclasses of a sealed class can be instantiated unless also restricted.
Vehicle myCar = Car();
​
extension VehicleSounds on Vehicle {
  String get sound {
    // ERROR: The switch does not exhaustively account for
    // all possible objects of type `Vehicle`.
    // In this example, a `Vehicle` with a run-time type of `Bicycle`
    // would not match any of the cases.
    return switch (this) {
      Car() => 'vroom',
      Truck() => 'VROOOOMM',
    };
  }
}

Se você não quiser switching exaustivo, ou quiser poder adicionar subtipos posteriormente sem quebrar a API, use o modificador final. Para uma comparação mais aprofundada, leia sealed versus final.

Combinando modificadores

#

Você pode combinar alguns modificadores para restrições em camadas. Uma declaração de classe pode ser, em ordem:

  1. (Opcional) abstract, descrevendo se a classe pode conter membros abstract e impede a instanciação.
  2. (Opcional) Um de base, interface, final ou sealed, descrevendo restrições em outras bibliotecas subtipificando a classe.
  3. (Opcional) mixin, descrevendo se a declaração pode ser misturada.
  4. A palavra-chave class em si.

Você não pode combinar alguns modificadores porque eles são contraditórios, redundantes ou mutuamente exclusivos:

  • abstract com sealed. Uma classe sealed é implicitamente abstract.
  • interface, final ou sealed com mixin. Esses modificadores de acesso impedem misturar.

Para mais orientações sobre como os modificadores de classe podem ser combinados, confira a Referência de modificadores de classe.

Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.