Here's a little fun class:
abstract class Concept {
late Enum option;
String get name => option.name;
}
and you might implement it like this:
enum FeeOption {
fast,
standard,
slow,
}
class FastFeeRate extends Concept {
FeeOption option = FeeOption.fast;
}
print(FastFeeRate().name); // 'fast'
but then you get an error:
FastFeeRate.option=' ('void Function(FeeOption)') isn't a valid override of 'Concept.option=' ('void Function(Enum)').
So, how do you specify a variable as any kind of enum, not Enum itself?
Your class Concept has a mutable (late, but that doesn't matter) field with type Enum. That means it has a setter named option= with an argument type of Enum.
The subclass FastFeeRate is a subclass. It has another field (your class has two fields!) also named option, which has a setter with an argument type of FastFeeRate.
That's not a valid override. The subclass setter must accept all arguments that the superclass setter does, but it doesn't accept all Enum values.
What you might have intended to do is:
abstract class Concept<T extends Enum> {
T option;
Concept(this.option);
String get name => option.name;
}
class FastFeeRate extends Concept<FeeOption> {
FastFeeRate() : super(FeeOption.fast);
}
or
abstract class Concept<T extends Enum> {
abstract T option;
String get name => option.name;
}
class FastFeeRate extends Concept<FeeOption> {
FastFeeRate option = FeeOption.fast;
}
depending on whether you want to define the field in the superclass or the subclass (but make sure to only define a concrete field in one of them).
Related
So, basically I need to create restrictions of which types can be used in a Type variable, something like this:
class ElementFilter<T extends Element> {
final Type<T> elementType; // What I want is something like Type<T>, but Type does not have a generic parameter
ElementFilter(this.elementType);
}
List<T> filterElements<T extends Element>(ElementFilter<T> element) {
return elements.where((el) => _isOfType(el, element.type)).toList();
}
filterElements(ElementFilter(ClassThatExtendsElement)); // Would work fine
filterELements(ElementFilter(String)); // Error, String does not extends Element
So it would only be possible to create ElementFilters with types that extend Element. Is this possible in some way?
I think you probably want:
/// Example usage: ElementFilter<ClassThatExtendsElement>();
class ElementFilter<T extends Element> {
final Type elementType;
ElementFilter() : elementType = T;
}
Unfortunately, there's no way to make the generic type argument non-optional. You will have to choose between having a required argument and having a compile-time constraint on the Type argument.
Dart doesn't support algebraic types, so if you additionally want to support a finite set of types that don't derive from Element, you could make specialized derived classes and require that clients use those instead of ElementFilter. For example:
class StringElementFilter extends ElementFilter<Element> {
#override
final Type elementType = String;
}
(You also could create a StringElement class that extends Element if you want, but at least for this example, it would serve no purpose.)
I highly recommend not using Type objects at all. Ever. They're pretty useless, and if you have the type available as a type parameter, you're always better off. (The type variable can always be converted to a Type object, but it can also be actually useful in many other ways).
Example:
class ElementFilter<T extends Element> {
bool test(Object? element) => element is T;
Iterable<T> filterElements(Iterable<Object?> elements) =>
elements.whereType<T>();
}
List<T> filterElements<T extends Element>(ElementFilter<T> filter) =>
filter.filterElements(elements).toList();
filterElements(ElementFilter<ClassThatExtendsElement>()); // Would work fine
filterElements(ElementFilter<String>()); // Error, String does not extends Element
I have a class with a nullable property. I would like to make a superclass that overrides that property with a non nullable one
so
class Example {
String? name;
}
class NamedExample extends Example {
#override
String name;
}
Is there some way to do that? if not how is this goal conventionally accomplished.
I basically want two identical classes except one of them always has a property while it is optional in another.
This is a place for the covariant keyword. Normally it does not make sense to override a parameter's type with its subtype and it is invalid to do so. This keyword tells the analyzer this is intentional. It can be added in either the super or subclass.
Subclass:
class Example {
String? name;
}
class NamedExample extends Example {
#override
covariant String name;
NamedExample(this.name);
}
Superclass:
class Example {
covariant String? name;
}
class NamedExample extends Example {
#override
String name;
NamedExample(this.name);
}
The reason why you can't override the String? name member with String name is because it can violate the contract of the setter in the base class and therefore could be unsafe. The base class advertises that:
var example = Example();
example.name = null;
is legal. However, if example instead is an instance of NamedExample, the example.name = null assignment would no longer be legal. The covariant keyword disables this safety check and trusts that you will never perform such an assignment in practice.
In general, you should avoid overriding fields.
You could safely have the override if your classes expose only a getter. Both of the following examples would be legal and safe:
class Example {
String? _optionalName;
String? get name => _optionalName;
}
class NamedExample extends Example {
NamedExample(this._requiredName);
String _requiredName;
#override
String get name => _requiredName;
}
or
class Example {
Example([this.name]);
final String? name;
}
class NamedExample extends Example {
NamedExample(this.name);
#override
final String name;
}
I want to specify a constructor that does not change the default value of test.
So "test text" should be printed instead of null.
void main() async {
print(B().text);
}
class A{
A({this.text = "test text"});
final text;
}
class B extends A{
B({String text}) : super(text: text);
}
dartpad
Is this possible?
Not as such. If you write a constructor, you can't get the superclass constructor's default value automatically in any way, and you have to either pass the parameter to the superconstructor or not in the initializer list. It can't depend on the value of the subclass constructor's parameter. So, you have to write the superclass constructor parameter default value again.
The one hack you can do (and I don't recommend doing it just for this) is that if the parameters to the subclass constructor is exactly the same as the superclass constructor, you can declare the members of your subclass in a mixin and make the subclass be a mixin application.
So instead of:
class SuperClass {
SuperClass(...args) : ...
...
}
class SubClass extends SuperClass {
SubClass(...args) : super(...args);
members() ...
}
you do:
class SuperClass {
SuperClass(...args) : ...
...
}
mixin _SubClass on SuperClass {
members() ...
}
class SubClass = SuperClass with _SubClass;
That will give SubClass a constructor for each superclass constructor, with the same parameters (including default values), which forwards directly to the superclass constructor.
Don't do this just to avoid writing the default value again!
Kennel has a Dog.
KennelWithPlayground (of type Kennel) has a DogWithABall (of type Dog) and cannot have just a normal Dog.
I want to be able to treat any type of Kennel like it has a normal Dog.
So, I would like to redefine a property of a super class, in a subclass. The new property will be a subclass of the property's superclass. Is this possible in Dart? (something like the 'new' modifier in c# maybe). Or is there another way to achieve this?
The following code does not work because in the KennelWithPlayground class, DogWithABall is not a valid override of Dog.
class Dog {
String get bark => "woof";
}
class DogWithBall extends Dog {
String get ballAction => "boing";
}
abstract class Kennel {
Dog dog;
}
class KennelWithPlayground implements Kennel {
DogWithBall dog; //**invalid override**
KennelWithPlayground(DogWithBall dog);
}
void processKennel(Kennel kennel){
kennel.dog.bark;
if (kennel is KennelWithPlayground)
print(kennel.dog.ballAction);
else
print("Bored dog");
}
There are two solutions that I can think of off the top of my head. You can use the covariant keyword to allow the override, at the expense of a runtime check, or you can use generics to track the types statically. For the former approach, you would change the definition of Kennel as follows:
abstract class Kennel {
covariant Dog dog;
}
Or alternatively, change the definition of KennelWithPlayGround:
class KennelWithPlayground implements Kennel {
covariant DogWithBall dog;
KennelWithPlayground(DogWithBall dog);
}
This will allow the override, but will cause a runtime check to happen on every write to the dog field to make sure that whatever is being written is actually a DogWithBall.
An alternative is to use generics as follows:
class Dog {
String get bark => "woof";
}
class DogWithBall extends Dog {
String get ballAction => "boing";
}
abstract class Kennel<DogKind extends Dog> {
DogKind dog;
}
class KennelWithPlayground implements Kennel<DogWithBall> {
DogWithBall dog;
KennelWithPlayground(DogWithBall dog);
}
void processKennel(Kennel kennel){
kennel.dog.bark;
if (kennel is KennelWithPlayground)
print(kennel.dog.ballAction);
else
print("Bored dog");
}
It is possible, but if and only if the property has no setter.
class Foo {
final num value;
Foo(this.value);
}
class Bar implements Foo {
final double value;
Bar(this.value);
}
I have an abstract superclass with a factory that returns an instance of a subclass. Is it possible to have a method that is implemented only in superclass? In the following code, for instance, would it be possible to remove Wind::act()?
abstract class Element {
final String action; // what it does
String act() => action; // do it
factory Element() {
return new Wind();
}
}
class Wind implements Element {
final action = "blows";
act() => action; // Why is this necessary?
}
void main() {
print(new Element().act());
}
When removing Wind::act(), there is an error about it missing. Also, when extending rather than implementing the superclass, leaving out the subclass implementation doesn't cause an error. But with a factory method, extending is not an option.
To inherit functionality from Element in Wind, you need to either extend or mix-in Element in Wind. Merely implementing an interface will not inherit any implementation.
So, you need to have class Wind extends Element { ... }.
That's not currently possible because Element has no generative constructor that Wind can use as super-constructor. So, you need to add that too, and make sure to initialize the action field in that constructor.
class Element {
final String action;
Element._(this.action); // Generative constructor that Wind can use.
factory Element() = Wind; // Factory constructor creating a Wind.
String act() => action;
}
class Wind extends Element {
Wind() : super._("blows");
}
The generative constructor doesn't need to be private, but if you are declaring and using all the classes only inside your own library, it might as well be.
Another option is to have a separate ElementBase class containing the action field and act function and an empty-named generative constructor. Mixins are not a good choice in this case, because there is no good way to make action final when mixins can't have constructors.
abstract class Element {
String get action;
factory Element() = Wind;
String act();
}
class ElementBase implements Element {
final String action;
ElementBase(this.action);
String act() => action;
}
class Wind extends ElementBase {
Wind() : super("blow");
}
It's a common problem to want both a generative constructor for subclasses and a factory constructor generating the default implementation in an interface/skeleton class. The List and Map interfaces have this problem, and have solved it by exposing ListBase and MapBase. I think that is the best solution when you are exposing the superclass to other users in other libraries. If it's only used internally by yourself, I'll use the private/non-default-named generative constructor in the superclass.