What's the rationale behind not inheriting static variables, in Dart? - class

In Dart, if one class extends another, the extended class inherits all of the super classes non-static variables, but inherits none of its static variables.
For example
class TestUpper {
static final String up = 'super';
String upup = 10;
}
class TestLower extends TestUpper {
static final String low = 'lower';
String lowlow = 11;
}
var lower = new TestLower();
print( lower.lowlow ); // <== 11
print( lower.upup ); // <== 10
print( TestLower.low ); // <== "lower"
print( TestLower.up ); // <== No static getter 'get:up' declared in class 'TestLower'
Is this the normal behavior? If so, I would appreciate if someone explained the rationale behind it.

Yes, there's no inheritance of static members. See Static Methods section of the language specification :
Inheritance of static methods has little utility in Dart. Static methods cannot be overridden. Any required static function can be obtained from its declaring library, and there is no need to bring it into scope via inheritance. Experience shows that developers are confused by the idea of inherited methods that are not instance methods.
Of course, the entire notion of static methods is debatable, but it is retained here because so many programmers are familiar with it. Dart static methods may be seen as functions of the enclosing library.

Related

Why is it necessary to use constructors in dart programming language classes?

I'm a beginner learning dart from the book dart apprentice and I reached where they were discussing constructors in dart classes, the book was implying that constructors create instances of the class which I understood but I needed more info about constructors. So I googled and some results repeated what was already in the book about it being used to create instances of a class while others also showed that it's used to instantiate class properties, but my problem is with the other answer which I found that they are used to instantiate properties of a class, but my question is: I instantiate all class properties when I create the class by declaring the property variables, like this:
class UserClass{
userClassProperty = "";
anotherUserClassProperty = ""; }
why is the constructor also needed to instantiate class properties?
Often, values are unique to every class instance.
Consider the following example:
class Point {
final int x;
final int y;
const Point(this.x, this.y);
double get distanceToOrigin => sqrt(x * x + y * y);
}
If the x and y values were defined inside the class, it would be pretty useless. Instead, different Point objects can be instantiated with different values, which means the same code can be used for different situations.
Ok, so constructors instantiate or start a class by collecting all the data the class needs to start to start working. Constructors are so important that the dart compiler provides one even if you don't explicitly create one. For example, you create a class for mammals like this :
class Mammal{
String name = "cat";
int numberOfLegs = 2;
}
Although you don't explicitly add a constructor the dart compiler adds a default constructor like this :
class Mammal{
Mammal(); //This is added by dart during the class instantiation by default.
String name = "cat";
int numberOfLegs = 2;
}
Yeah, that's how crucial constructors are to the dart compiler.
And on the topic of why are they necessary even when you declare all the properties by yourself in the class, as hacker1024 said it would make the class pretty useless, as the point of the existence of classes is to create variants but with different properties. Not adding a constructor to your class and defining all the properties in the class would mean that your class doesn't take property arguments which in turn also means that different variants of your class can't be created. Again this goes directly against the point of the existence of dart classes. For example, you have a class like this :
class Mammals{
Strig name = "Human";
int numberOfLegs = 2;
bool hasFur = false;
}
final cat = Mammal();
final human = Mammal();
print(cat.numberOfLegs); //Prints 2
//2
print(human.numberOfLegs); //Also prints 2
//2
print(cat.hasFur);
// false
Yeah, this class is problematic. Cats with 2 legs? You would agree with me that that's not how things are in reality. And also the class is pretty useless in the sense that it's not modular, no matter which kind of mammal we create be it a cat, a sheep or even a cow the name property is going to be the default one we set, that is "Human". When we create a class to simulate mammals we want to be able to define what kind of properties it has, not use some fixed values. So you want to create a class which has a constructor like this :
class Mammals{
Mammals(String name,int noOfLegs, bool hasFur){
this.name = name;
this.noOfLegs = noOfLegs;
this.hasFur = hasFur;
}
String name = "";
int noOfLegs = 0;
bool hasFur = False;
}
final cat = Mammal("Cat", 4, True); //Now you can pass in the properties ou want.
final human = Mammal("Human", 2, false);
print(cat.name); //This prints the customized name of the object cat instead of some fixed value
//Cat
print(human.name); //This prints the customized name of the object human
Now we have two instances of the class with separate property values.
Although this adds a little more code, the modularity benefit is worth it.

Best practice for overriding class fields in Dart

I'm in the middle of creating a theming system for my app, where I wish to create a base theme class, with fields for colour and fonts:
abstract class BaseTheme {
late Color backgroundColor;
late Color appBarBackgroundColor;
late Color progressIndicatorColor;
late Color primaryColor;
late Color iconColor;
}
(I have to define them as late to keep the Null Safety happy! These have to be overridden anyway, so no problems there).
I would then implement this base class for each theme, as below:
import 'base_theme.dart';
class LightTheme implements BaseTheme {
Color backgroundColor = Colors.white;
Color appBarBackgroundColor = Color(0xff1a1818);
Color progressIndicatorColor = Color(0xffffc114);
Color primaryColor = Colors.red;
Color iconColor = Colors.white;
}
I would then use these values to populate the ThemeData:
BaseTheme _lightTheme = new LightTheme();
ThemeData _theme = ThemeData(
scaffoldBackgroundColor: _lightTheme.backgroundColor,
// etc...
}
Now, this appears to work fine. However, the page on overriding fields in the Linter for Dart states that overriding fields is bad practice. So, is there a better way to achieve something similar?
What's confusing is that, on that page, one example shows overriding fields when you extend a base class, which is considered bad. However, overriding fields when you implement a base class is good - But in this case, the field type has changed from Base to Derived (ie. they're not the same).
As the lint recommends, you should avoid overriding fields. Your abstract class should define an interface; as such, it should declare getters and setters and leave the implementation to the concrete, derived classes. Note that this avoids the need for late (which is an implementation detail that does not belong in the abstract base class).
abstract class BaseTheme {
Color get backgroundColor;
set backgroundColor(Color value);
Color get appBarBackgroundColor;
set appBarBackgroundColor(Color value);
// etc.
}
Dart 2.13 allows members to be declared as abstract, so you can use that to avoid the repetitiveness:
abstract class BaseTheme {
abstract Color backgroundColor;
abstract Color appBarBackgroundColor;
// etc.
}
Also see the GitHub issue: overridden_fields documentation is vague.
The reason it's not recommended as in the rule is because of memory allocation. When you override a field of an extended class you allocate more memory. For example:
class Base {
Object field = 'lorem';
Object something = 'change';
}
class Bad1 extends Base {
#override
final field = 'ipsum'; // LINT
}
In the example above when you create a new instance of Bad1 you will get field and super.field. So you allocated memory for two Object instances when you probably only needed one. You can read more about it here https://github.com/dart-lang/linter/issues/230 and here https://github.com/dart-lang/linter/issues/2428
You bring up a good point about the change in type in the docs in this example
abstract class BaseLoggingHandler {
Base transformer;
}
class LogPrintHandler implements BaseLoggingHandler {
#override
Derived transformer; // OK
}
These docs are old so this is pre null safety. Now this is a compile type error because to narrow the type you need to be more explicit and use the covaraint keyword. Like so
abstract class BaseLoggingHandler {
BaseLoggingHandler(this.transformer);
Base transformer;
}
class LogPrintHandler implements BaseLoggingHandler {
LogPrintHandler(this.transformer);
#override
covariant Derived transformer; // OK
}
Here is an example to illustrate the concept of a covaraint.
Given
abstract class Base {}
class Derived extends Base{}
the following
typedef Example = void Function(Derived);
Example e = (Base b) {};
This is valid because a Derived is a Base and if you were to give me a function (Base b) {} I can safely invoke your function passing an object of type Derived and I know operations you will take upon that object are safe.
But this is not valid for the opposite reason.
typedef Example = void Function(Base);
Example e = (Derived d) {};
Yes... Whatever you have done is correct. The link https://dart-lang.github.io/linter/lints/overridden_fields.html says its bad practice to override the class not the abstract class. When we are extending the abstract class, its compulsion to override its fields and methods. See the below screenshots.
Here are the more lint: rules https://dart.dev/tools/linter-rules

instantiate object with *new* keyword and use property inside class with *this* keyword

i'm coming from mainly JS/TS world (NestJS/Angular) and recently i start to building Flutter apps..
i have 2 main questions
there is any difference when instantiate object with or without new keyword?
i saw examples in flutter when people use new Row(children: [Text('Foo'), Text('Bar'),],) instead of just Row(...)
if there is a difference which one is better to use?
inside of my Dart classes in flutter app, i can both use this.property and property again there is any difference and if so which one is better and why?
example:
class Person {
final String name;
final int age;
Person(this.name, this.age);
getNameAge() => '${this.name} is ${this.age}';
getNameAge2() => '$name is $age';
}
both looks the same to me
void main() {
final p = Person('dan', 22);
final p2 = new Person('ben', 20);
print(p.getNameAge()); // dan is 22
print(p2.getNameAge2()); // ben is 20
}
The new keyword is optional in Dart and I think the general consensus is, today, to not use it.
The use of this is useful if you have multiple variables with the same name but in different scope. E.g. (this is just an example. You would not make a setA method in Dart but use properties):
class A {
int a;
A(this.a);
void setA(int a) {
this.a = a;
}
}
Here we use this to distinguish between the argument a and the class variable a. But if you don't have variables with the same name (but in different scope), the use of this is optional. In some projects, you still use this to make it more clear that you are referring to a class variable even if it is not needed.

How to declare final class in Dart to prevent extending from it?

In Java\Kotlin we have a String class that is final and immutable.
I tried to mark the class with final keyword but looks like it's not allowable.
So, I'm a little bit confusing, how to declare final class in Dart ?
Note: the case is - I want to instantiate this class outside, but forbid to extending it. So using the private constructor - it's not my case.
You can achieve this final effect from java by having a private constructor for your class, it will prevent the class from being extended, BUT it will also prevent the class from being instantiated (only in the same file both will be possible):
class MyString {
MyString._(); // use _ for private constructor.
static void print(String s) {
print(s);
}
}
Call with
String message = "Hello World";
MyString.print(message);
Dart considers that we are all adults, preventing class extension is hence part of the design and responsability of the developers to have clear class names, and not part of the language:
AVOID extending a class that isn’t intended to be subclassed.
If a constructor is changed from a generative constructor to a factory constructor, any subclass constructor calling that constructor will break. Also, if a class changes which of its own methods it invokes on this, that may break subclasses that override those methods and expect them to be called at certain points.
Difference of meaning for final with Java
Dart has a very simple definition of what is final: a variable in dart can only be set once, id est: is immutable.
Final and const
If you never intend to change a variable, use final or const, either instead of var or in addition to a type.
A final variable can be set only once; a const variable is a compile-time constant. (Const variables are implicitly final.) A final top-level or class variable is initialized the first time it’s used.
Additionally to the approach of making the constructor private and instantiating your object via a static factory, you could use the package meta and
annotate your final class as sealed:
#sealed
class Z{}
This will signal users of your package that this class should not be extended or implemented. For example in vscode trying to extend the class Z:
class Z1 extends Z{}
results in the following warning:
The class 'Z' shouldn't be extended, mixed in,
or implemented because it is sealed.
Try composing instead of inheriting, or refer
to its documentation for more information.dart(subtype_of_sealed_class)
The issue will also be picked up by the dart analyzer:
$ dart analyze
Analyzing test... 0.8s
info • lib/src/test_base.dart:3:1 •
The class 'Z' shouldn't be extended, mixed in, or implemented because it
is sealed. Try composing instead of inheriting, or refer to its
documentation for more information. • subtype_of_sealed_class
You can use the factory unnamed constructor along with private named constructor, like this:
class NonExtendable {
NonExtendable._singleGenerativeConstructor();
// NonExtendable();
factory NonExtendable() {
return NonExtendable._singleGenerativeConstructor();
}
#override
String toString(){
return '$runtimeType is like final';
}
}
In a client code, in the same library, or another library, an instance can be created, an example:
// Create an instance of NonExtendable
print ('${NonExtendable()}');
Trying to extend it, something like
class ExtendsNonExtendableInSameLibrary extends NonExtendable {
ExtendsNonExtendableInSameLibrary._singleGenerativeConstructor() : super._singleGenerativeConstructor();
factory ExtendsNonExtendableInSameLibrary() {
return ExtendsNonExtendableInSameLibrary._singleGenerativeConstructor();
}
}
will work in the same library (same 'source file') but not in another library, making the class NonExtendable same as 'final' in Java from the perspective of any client code.

Declare variable outside any class: why is it possible

I have a file fancy_button.dart for a custom Flutter widget FancyButton which is like:
class FancyButton extends StatefulWidget {
// ...
}
class _FancyButtonState extends State<FancyButton> {
// ...
}
// Declaration outside any class:
Map<_FancyButtonState, Color> _buttonColors = {};
final _random = Random();
int next(int min, int max) => min + _random.nextInt(max - min);
// ...
The application works just fine. Notice that I declare and use some variables outside any class. Now my question is: how is it even possible? Shouldn't everything be inside a class in Dart, like Java?
No, Dart supports variables and functions defined in global space. You can see this with the main() method which are declared outside any class.
Also, global variables (and static class variables) are lazy evaluated so the value are first defined when you are trying to use them. So your runtime are not going to slow down even if there are a bunch of global variables there are not used.
Are you coming from Java before touching Dart?
Basically, Dart is not single-class-single-file like how Java works. Yes, it does support Object Oriented Programming (in kinda different way). The behavior of constructor is different. There is no public, private, and protected keywords. Please just refer to the official docs.
Anyway, you don't need a complex public static void main(). The real entry point is main(). Unless you define that function, you won't be able to run a file in command line.