I am new to Dart and Flutter. While I am going through tutorials, I got that we can make singleton using factory keyword. But after that, I got this code.
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
My questions.
How does the code work?
when getInstance() get called?
is AccountService._internal() a constructor?
static final _instance = AccountService._internal(); - When this get called?
Please help me
Static fields in Dart are all lazy evaluated so they will first get its value the first time you access the field.
So:
When you call getInstance(), it will return the value of the field _instance. If this is the first time the field will be evaluated so AccountService._internal() is called. If it is second time, the value from previous access is reused.
First time you call the method somewhere in your code? If you are never calling the method, the object referenced by _instance will never be created.
Yes, it is a named constructor and because the name starts with "_" it is only available from the library this class is part of. By doing so, it is possible to restrict new objects from this class so only the class itself are allowed to create an instance.
It is called first time _instance is accessed. Since this name also starts with "_" it is only available from the library this class is part of.
The lazy initialization of static fields is described in the Dart specification with the following reasoning:
Static variable declarations with an initializing expression are initializedlazily (8.1).
The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. This is especially crucial for Dart, which must support the coding of client applications.
https://dart.dev/guides/language/specifications/DartLangSpec-v2.2.pdf
Added code example
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal() {
print(':: Calling AccountService._internal constructor');
}
static AccountService getInstance() {
print(':: Calling getInstance()');
return _instance;
}
}
void main() {
print(':: Step 1');
AccountService.getInstance();
print(':: Step 2');
AccountService.getInstance();
print(':: End');
}
Output:
:: Start
:: Step 1
:: Calling getInstance()
:: Calling AccountService._internal constructor
:: Step 2
:: Calling getInstance()
:: End
Related
I'm trying to call a static method from a generic type I receive.
Is that even possible?
Furthermore, I apply a Type constraint in order to only manipulate the object from its parent class.
Here is a short example of what I'm trying to achieve:
class A {
static func() {
print("A");
}
}
class B extends A {
static func() {
print("B");
}
}
concret<T extends A>() {
T.func(); // I expected a print('B')
}
main() {
concret<B>();
}
No, it's not possible.
Dart static method invocations are resolved at compile-time, so it's not possible to call them on type variables which only have a value at run-time.
If it was possible, it would be completely unsafe. Anyone can create a class C extending A which does not have a static func member and invoke concret<C>();. Since static members are not inherited, it would have to give you a run-time error, and there is nothing you can do to detect that at compile-time. That is the primary reason why it is not allowed.
In Dart, is there a difference in assigning values right away vs in constructor like in Java?
class Example {
int x = 3;
}
vs
class Example {
int x;
Example() {
x = 3;
}
}
I ask because when I was using Flutter and tried to assign a Function that uses setState to a variable, it was not possible with the former method but possible with the latter.
In your trivial case, it doesn't matter.
In general, you can initialize instance variables in a few ways:
Inline (field initializers)
class Example1 {
T x = value;
}
Advantages:
Direct, concise.
Member will be initialized in all constructors.
Can be used to initialize final or non-nullable members.
Member is initialized before invoking base class constructors, which is important when the base class constructor calls member functions that are overridden by the derived class.
Disadvantages:
Cannot depend on construction arguments.
Usually cannot depend on this since the initialization occurs before this becomes valid (i.e., cannot depend on other instance members). (An exception is if the member is initialized lazily by declaring it late. This requires the null-safety feature to be enabled.)
Initializer list
class Example2 {
T x;
Example2() : x = value;
}
Advantages:
Can be used to initialize final or non-nullable members.
Member is initialized before invoking base class constructors, which is important when the base class constructor calls member functions that are overridden by the derived class.
Can utilize construction arguments.
The initialized variable always refers to a member variable, never to a constructor parameter.
Disadvantages:
If the class has multiple constructors, initialization would need to be duplicated, or constructors should redirect to a common constructor.
Cannot depend on this since the initialization occurs before this becomes valid (i.e., cannot depend on other instance members).
Can initialize only members of the enclosing class. Because initializer lists are executed before invoking base class constructors, they cannot set base class members.
Constructor body
class Example3 {
T x;
Example3() {
x = value;
}
}
Advantages:
Can utilize construction arguments.
Can be used to perform more complicated initialization, such as cases where the member cannot be initialized via a single expression.
Can use this (i.e., can use other instance members).
Can be used to set base class members.
Disadvantages:
Cannot be used to initialize non-late final nor non-nullable members.
If the class has multiple constructors, initialization would need to be duplicated or initialization code would need to be refactored out (such as, but not limited to, redirecting to a common constructor).
Member is initialized after invoking base class constructors.
If the constructor has a parameter that shadows a member variable, it's easy to accidentally refer to the parameter instead of the member. (See https://github.com/dart-lang/linter/issues/2552 for details.)
There probably are some points I'm forgetting, but I think that should cover the main ones.
Direct, inline initialization occurs first, then initialization lists, then constructor bodies. Also see Difference between assigning the values in parameter list and initialiser list, which explains why this becomes valid only for the later stages of object initialization.
As an example where it matters where members are initialized:
class Base {
Base() {
doSomething();
}
void doSomething() {}
}
class DerivedEarly extends Base {
int? x;
DerivedEarly() : x = 42;
#override
void doSomething() => print(x);
}
class DerivedLate extends Base {
int? x;
DerivedLate() {
x = 42;
}
#override
void doSomething() => print(x);
}
void main() {
DerivedEarly(); // Prints: 42
DerivedLate(); // Prints: null
}
I'm working on a library, and I have a implementation pattern users are required to follow:
class MyView extends LibView {
static Foo f = Foo();
#override
void render(){
use(f); // f should be static, otherwise things not work correctly
}
}
I would like to tell the compiler that, if someone ever does this, it's incorrect:
class MyView {
Foo f = Foo(); // Error: Foo can only be used in Static field.
...
}
Anyone know if this is possible? I find it really hard to find good docs on these sorta of language details when it comes to dart.
[EDIT] Since the "why" question always comes up, imagine something like:
class ViewState{
Map<int, Object> props = {};
}
ViewState _state = ViewState();
class View {
View(this.state);
ViewState state;
static int _key1 = getRandomInt();
void render(){
print(state(_key1))
}
}
// These should both print the same value off of state since the 'random' int is cached
View(_state);
View(_state);
If the key's were not static, everything would compile fine, but they would not print the same results.
What you properly need are a singleton which can be created in different ways in Dart. One way is to use a factory constructor like this:
class Foo {
static final Foo _instance = Foo._();
factory Foo() => _instance;
// Private constructor only used internally
Foo._();
}
void main() {
final a = Foo();
final b = Foo();
print(identical(a, b)); // true
}
By doing it like this, there will only be one instance of Foo which are then shared each time an instance are asked for. The instance are also first created the first time it is asked for since static variables in Dart are lazy and only initialized when needed.
I just want to do the functional equivalent of
int someUniqueKey = 0, or MyViewEnums.someUniqueKey but do it with a typed object rather than a int/enym, like: Object<Foo> someUniqueKey = Object<Foo>(). In order for this to work with Objects, it needs to be static. It's similar to how int someUniqueKey = random.nextInt(9999) would have to be static in order to be used as a key that all instances could share. That way keys are auto-managed and unique, and people don't need to assign int's, strings, or whatever. It also has the advantage of letting me use the type later for compile time checks.
bool prop = getPropFromRef(_prop1Ref); //Will throw error prop1Ref is not Ref<bool>
I think I've figured out something that does the trick using darts package-level methods.
class Ref<T> {}
// Re-use existing ref if it already exists
Ref<T> getRef<T>(Ref<T> o) => o ?? Ref<T>();
class RefView {}
// In some other package/file:
class MyView extends RefView {
static Ref<bool> prop1Ref = getRef(prop1Ref);
static Ref<int> prop2Ref = getRef(prop2Ref);
}
This will make sure that prop1 and prop2 have the same values across all instances of MyView and it will throw an error if these are not static (since you can not pass an instance field before Constructor)
This still has the downside of a potential hard to spot error:
class MyView extends RefView {
static Ref<bool> prop1 = getRef(prop1);
static Ref<bool> prop2 = getRef(prop1); // passing prop1 to prop2's getRef, and they have the same<T>, compiler will miss it
}
But I think it might be preferable than having this potential error:
class MyView extends RefView {
//Both of these will fail silently, keys will change for each instance of MyView
Ref<bool> prop1 = getRef(prop1);
Ref<bool> prop2 = getRef(prop2);
}
I have a Singleton patter class in my Jenkins shared library:
public class Configuration {
private static final INSTANCE = new Configuration()
static getInstance() { return INSTANCE }
private Configuration() {
}
def initialize(env, params) {
Foo = params.FOO;
}
public String Foo = ''
}
Later I can call this from elsewhere using something like this:
Configuration.instance.initialize(env, params);
config = Configuration.instance;
println 'FOO: ' + config.Foo
Ideally, I want the benefit of the Singleton pattern, but I don't want some fields to be overridden by consumers.
First Attempt:
On first thought, I would think this would work:
public class Configuration {
private static final INSTANCE = new Configuration()
static getInstance() { return INSTANCE }
private Configuration() {
}
def initialize(env, params) {
INSTANCE.#Foo = params.FOO;
}
public final String Foo = ''
}
Error:
groovy.lang.GroovyRuntimeException: Cannot set the property 'Foo' because the backing field is final.
Second Attempt:
On Second thought, I would think initializing in the constructor would work, however I don't seem to have access to params and env, unless these are passed in from the vars function, via the initialize() method.
How can I make this Singleton class immutable, or its fields read only?
I think you Could:
Define your class with "implements Serializable", as documentation advices.
Implement the constructor that would accept 1 parameter of type BaseScript, and pass this to it upon instantiation, relative to that this (which you could call internal script) you can refer to script.params, script.env, etc. and I mean you don't HAVE to use initialize, you can do all you want in the c'tor.
But wait, please tell more:
why does CI/CD code need to have a Singleton?
You're passing its data as parameters [so it's not really an immutable entity :)]
Maybe you could "simply" create an immutable map out of your parameters....
Configuration as singleton feels as if you can delegate configuration management to ... configuration management service (consul, etcd, or others).
Please elaborate, it's very curious!
Also you referred to something as "consumers". are these library consumers? or people running the jobs?
Thank you!
I am using vala.
This is the source code that gives that compile time bug :
private Gee.HashMap<string,VoidFunc> fill_actions()
{
var actions = new Gee.HashMap<string,VoidFunc>();
MainWindow win = window;
actions["t"] = () => _puts(win.title);
return actions;
}
First I tried to access this.window directly but that gave another error so I tried this with a local scope variable.
Error when doing directly this.window :
This access invalid outside of instance methods
It sounds like VoidFunc is declared with [CCode (has_target = false)]. What that means is that no context information is passed to it, and AFAIK that is the only way delegates work as generic type arguments. The reason for this is limitations in C, so assuming VoidFunc looks like this:
[CCode (has_target = false)]
public delegate void VoidFunc ();
What you'll get in C is something like this:
typedef void (*VoidFunc)();
As opposed to something like this if you didn't have the [CCode (has_target = false)]:
typedef void (*VoidFunc)(gpointer user_data);
When you pass around callbacks in C you generally do so with between one and three arguments. Something with all three would look like this:
void foo (VoidFunc void_func, gpointer user_data, GDestroyNotify notify);
The first parameter is the actual function. The second parameter is the value to pass as user_data to the callback, and is what Vala uses to pass context information to the callback (which is what allows it to act as an instance method, or even a closure). The third parameter is used to specify a function to free user_data when it is no longer needed.
What [CCode (has_target = false)] means is that the delegate doesn't have a user_data argument, and therefore cannot be used as a closure or instance method.
The reason this is necessary with a generic argument is that generics look something like this at the C level:
void foo_bar (gpointer data, GDestroyNotify notify);
The first parameter is the data that you want to use as a generic value, the second is actually only added if the generic argument is owned (as it is in the case of the set methods in Gee), and is called with user_data as an argument when user_data is no longer needed.
As you can see, when trying to use a delegate as a generic, there is nowhere to put the user_data argument, which is why Vala only allows delegates without targets to be generic arguments.
The solution is basically to wrap the delegate in a class:
public delegate void VoidFunc ();
public class YourClass {
private class VoidFuncData {
public VoidFunc func;
public VoidFuncData (owned VoidFunc func) {
this.func = (owned) func;
}
}
private Gee.HashMap<string,VoidFuncData> fill_actions() {
var actions = new Gee.HashMap<string,VoidFuncData>();
string win = "win";
actions["t"] = new VoidFuncData (() => GLib.debug (win));
return actions;
}
}