Both Java and Javascript allow for a different way of executing static code. Java allows you to have static code in the body of a class while JS allows you to execute static code outside class definitions. Examples:
Java:
public class MyClass {
private static Map<String,String> someMap = new HashMap<String,String();
static {
someMap.put("key1","value");
someMap.put("key2","value");
SomeOtherClass.someOtherStaticMethod();
System.out.println(someMap);
}
}
JS (basically any JS code outside a class):
var myint = 5;
callSomeMethod();
$(document).ready(function () {
$("#hiddenelement").hide();
});
However, it seems like Dart supports either of both ways. Declaring global variables and methods is supported, but calling methods and executing code like in JS is not. This can only be done in a main()
method. Also, static code inside a class is not allowed either.
I know Dart has other ways to statically fill a Map like my first example, but there is another case that I can think of for which this is required.
Let's consider the following CarRegistry
implementation that allows you to map strings of the car model to an instance of the corresponding class. F.e. when you get the car models from JSON data:
class CarRegistry {
static Map<String, Function> _factoryMethods = new HashMap<String, Function>();
static void registerFactory(String key, Car factory()) {
_factoryMethods[key] = factory;
}
static Car createInstance(String key) {
Function factory = _factoryMethods[key];
if(factory != null) {
return factory();
}
throw new Exception("Key not found: $key");
}
}
class TeslaModelS extends Car {
}
class TeslaModelX extends Car {
}
In order to be able to call CarRegistry.createInstance("teslamodelx");
, the class must first be registered. In Java this could be done by adding the following line to each Car
class: static { CarRegistry.registerFactory("teslamodelx" , () => new TeslaModelX()); }
. What you don't want is to hard-code all cars into the registry, because it will lose it's function as a registry, and it increases coupling. You want to be able to add a new car by only adding one new file. In JS you could call the CarRegistry.registerFactory("teslamodelx" , () => new TeslaModelX());
line outside the class construct.
How could a similar thing be done in Dart?
Even if you would allow to edit multiple files to add a new car, it would not be possible if you are writing a library without a main()
method. The only option then is to fill the map on the first call of the Registry.createInstance()
method, but it's no longer a registry then, just a class containing a hard-coded list of cars.
EDIT: A small addition to the last statement I made here: filling this kind of registry in the createInstance()
method is only an option if the registry resided in my own library. If, f.e. I want to register my own classes to a registry provided by a different library that I imported, that's no longer an option.
The static methods can be called by using the class name, which they belong to instead of creating an object. Syntax: className. staticMethod();
"static" means a member is available on the class itself instead of on instances of the class. That's all it means, and it isn't used for anything else. static modifies members.
Why all the fuss about static?
You can create a getter that checks if the initialization was already done (_factoryMethods != null
) if not do it and return the map.
As far a I understand it, this is all about at what time this code should be executed.
The approach I showed above is lazy initialization.
I think this is usually the preferred way I guess.
If you want to do initialization when the library is loaded
I don't know another way as calling an init()
method of the library from main()
and add initialization code this libraries init()
method.
Here is a discussion about this topic executing code at library initialization time
I encountered the same issue when trying to drive a similarly themed library.
My initial attempt explored using dart:mirrors
to iterate over classes in a library and determine if they were tagged by an annotation like this (using your own code as part of the example):
@car('telsamodelx')
class TelsaModelX extends Car {
}
If so, they got automatically populated into the registry. Performance wasn't great, though, and I wasn't sure if how it was going to scale.
I ended up taking a more cumbersome approach:
// Inside of CarRegistry.dart
class CarRegister {
static bool _registeredAll = false;
static Car create() {
if (!_registeredAll) { _registerAll()); }
/* ... */
}
}
// Inside of the same library, telsa_model_x.dart
class TelsaModelX extends Car {}
// Inside of the same library, global namespace:
// This method registers all "default" vehicles in the vehicle registery.
_registerAll() {
register('telsamodelx', () => new TelsaModelX());
}
// Inside of the same library, global namespace:
register(carName, carFxn) { /* ... */ }
Outside of the library, consumers had to call register();
somewhere to register their vehicle.
It is unnecessary duplication, and unfortunately separates the registration from the class in a way that makes it hard to track, but it's either cumbersome code or a performance hit by using dart:mirrors.
YMMV, but as the number of register-able items grow, I'm starting to look towards the dart:mirrors
approach again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With