What is 'extension' in dart? - dart

Explain what's going on here in the following snippet.
enum WeatherStatus { initial, loading, success, failure }
extension WeatherStatusX on WeatherStatus {
bool get isInitial => this == WeatherStatus.initial;
bool get isLoading => this == WeatherStatus.loading;
bool get isSuccess => this == WeatherStatus.success;
bool get isFailure => this == WeatherStatus.failure;
}

An extension declaration associates members to a type.
Normally, in Dart, a method is either associated with a class or interface (instance methods) or with a static (class/mixin/library/etc) namespace (static methods).
An extension declaration instead associates methods directly with a type, which doesn't have to be an interface type (but can be).
When you try to call a method on something typed as WeatherStatus, say
var status = WeatherStatus.initial;
print(status.isInitial);
the compiler first checks whether the WeatherStatus interface has an isInitial instance member.
Since it doesn't, it then goes on to check if there are any extension methods associated with the WeatherStatus type.
There is (because the WeatherStatusX extension is in scope), and then it instead calls WeatherStatusX(status).isInitial which invokes that method with this bound to the status object.
It's a way to add members on a type from the side, without needing to add them to the class itself (which you can't if it's not your class).
The trade-off is that extension methods are not virtual. You can't add an override in a subclass, and get it called even if the caller only know the object at the superclass type, like you can for classes.
In reality, extension methods are most like static helper functions. You could write:
bool isInitial(WeatherStatus self) => self == WeatherStatus.initial;
and call it as isInitial(status). The extension declaration allows you to pretend that a static helper function is more like a special instance method, because you call it as status.isInitial, but it really isn't an instance method. It just looks like one.
In this particular case, I'd recommend using the newest version of the Dart SDK so you can use the new enhanced enum feature:
enum WeatherStatus {
initial, loading, success, failure;
bool get isInitial => this == initial;
bool get isLoading => this == loading;
bool get isSuccess => this == success;
bool get isFailure => this == failure;
}
This makes the isInitial, etc., getters actual instance members on the enum class. (Still can't override them, since enums can't have subclasses, but you don't need to worry about whether extension WeatherStatusX is imported before you use them.)

Related

Is there a way to access a mixin's private variable in the class using the mixin?

In dart when creating a mixin, you can declare properties and methods like a class. When declaring a private property/method, it seems the inheriting class should also have access to this private member (see below for example).
Is there a way to access a mixin's private variable in the class using the mixin?
If it's not possible, how can I declare a member in the mixin object but make it private in the inheriting class's interface.
mixin.dart
mixin A {
String propertyOne = '1';
// This property is not accessible to any inheriting class.
int _privateProperty = 2;
}
class.dart
class B with A {
String get mixinString => propertyOne;
// This property is not accessible to the B class.
int get mixinInt => _privateProperty;
}
No. A property being library private means that you can only express its name inside the same library. In any other library, the identifier _privateProperty is a different name, one private to that other library.
If you cannot declare both mixin and class in the same library, and you definitely need access to the property, then you can do any number of things to allow that.
Make the property public and tell people not to use it except in subclasses. They still can if they want to.
Make the property public and mark it #protected, to have the analyzer tell people to not use it except in subclasses. They still can if they want to.
Keep the property private and provide a separate method to access it:
mixin A {
// This property is not accessible to any inheriting class.
int _privateProperty = 2;
static int getPrivateProperty(A a) => a._privateProperty;
static void setPrivateProperty(A a, int value) {
a._privateProperty = value;
}
}
Anyone can still get to the property if they really want to, but they need to know that
it comes from A.

How to pass a class to a method and create it there

I have such an example of a model:
class BirthdayModel {
List birthdays;
BirthdayModel({
#required this.birthdays,
});
factory BirthdayModel.fromJson(json){
return BirthdayModel(birthdays: json['data']);
}
Map<String, dynamic> toJson() {
return {
'birthdays': birthdays,
};
}
}
I want to transfer multiple models into one method:
exampleMethod(model: BirthdayModel);
and then in this method call the constructors or methods of the passed class
exampleMethod(#required model){
return model.fromJson(data);
}
Is it possible to do this?
Not the way you write it.
You cannot pass a class as argument. Even type arguments only pass types, so you cannot use static members that way.
What you can do is:
T exampleMethod<T>(T createModelFromJson(dynamic json)){
return createModelFromJson(data);
}
and call it as :
var birthday = exampleMethod(BirthdayModel.fromJson);
There is no way to access the fromJson programmatically - it's not an instance method so there is no interface for it. Static methods must be accessed explicitly.
(I'm ignoring dart:mirrors because you probably won't have access to those).

Overloading a method in Groovy using Closure arguments with different return types

I'm reasonably proficient with Groovy insofar as my job requires, but not having a background in OOP means that some things still elude me, so apologies if some of the wording is a little off here (feel free to edit if you can make the question clearer).
I'm trying to create an overloaded method where the signature (ideally) differs only in the return type of the single Closure parameter. The Closure contains a method call that returns either an ItemResponse or ListResponse object, both of which could contain an object/objects of any type (which is the type I would like to infer).
The following code is a simplified version of what I'm trying to implement - an error handling method which takes a reference to a service call, safely attempts to resolve it, and returns the item/items from the response as appropriate:
public <T> T testMethod(Closure<ItemResponse<T>> testCall) {
testCall.call().item as T
}
public <T> List<T> testMethod(Closure<ListResponse<T>> testCall) {
testCall.call().items as T
}
Obviously this doesn't work, but is there any alternate approach/workaround that would achieve the desired outcome?
I'm trying to create an overloaded method where the signature
(ideally) differs only in the return type of the single Closure
parameter.
You cannot do that because the return type is not part of the method signature. For example, the following is not valid:
class Demo {
int doit() {}
String doit() {}
}
As mentioned by yourself and #jeffscottbrown, you can't have two methods with the same parameters but different return value. The workaround I can see here is to use a call-back closure. The return value of your testMethod would default to Object and you would provide an "unwrapper" that would the bit after the closure call (extract item or items). Try this out in your GroovyConsole:
class ValueHolder <T> {
T value
}
Closure<List<Integer>> c = {
[1]
}
Closure<ValueHolder<String>> d = {
new ValueHolder(value:'hello world')
}
Closure liu = {List l ->
l.first()
}
Closure vhsu = {ValueHolder vh ->
vh.value
}
// this is the generic method
public <T> Object testMethod(Closure<T> testCall, Closure<T> unwrapper) {
unwrapper(testCall.call()) as T
}
println testMethod(c, liu)
println testMethod(d, vhsu)
It works with both a list or a value holder.

Lazy/inline implement a protocol in Swift

I want to lazy/inline implement a protocol in Swift.
So in the point of the implementation I will have access to variables outside the protocol scope ,
Same as implementing a interface in Java without declaring a class:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ?
func a0() {MyClass.count--}
func a1() {MyClass.count++}
}
someComponenet.delegate = a;
}
protocol SomeProtocol {
func a0()
func a1()
}
editing----
thanks i look at this solution, and i didn't see how to access a variable of the parent class.
all the examples show an Anonymous class but no one of the examples is accessing the parent variables .
What you're looking for is an inner class (not necessarily an anonymous one), declared in a scope that lets it access the count variable of a MyClass instance, and that adopts a protocol defined at a different scope. Right now Swift has a few of those pieces, but it doesn't look like you can put them all together in any way that's as concise as what you might be looking for.
You might think about declaring an inner class:
class MyView: UIView {
let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred
var count = 0 // type Int is inferred
class Helper: SomeProtocol {
func a0() { count-- } // ERROR
// ...
}
init() {
someComponent.delegate = Helper()
}
}
But that won't work, because count is implicitly self.count, where self is a Helper instance, not the MyView instance that "owns" the Helper instance. And there isn't a way to reference that MyView instance (or its properties) from within a Helper's methods, because you could just as well construct a MyView.Helper() without having an existing MyView instance. Inner classes (or nested types in general) in Swift nest only in lexical scope, not in existential ownership. (Or to put it another way, since you referenced Java: all inner classes in Swift are like static inner classes in Java. There's no non-static inner class.) If that's a feature you'd like, though, it's probably worth telling Apple you want it.
You could also try declaring Helper inside MyView.init() -- in Swift you can nest type definitions anywhere, including inside functions or methods of other types. Defined there, it can refer to MyView's properties. However, now the type information for Helper is only visible inside of MyView.init(), so when you assign it to someComponent.delegate (whose type is just SomeProtocol), you can't make use of it... this crashes the compiler, even. (That's another bug to report, but it's hard to say whether the bug is really "compiler crashes on valid usage" or "code is bad, but compiler crashes instead of producing error".)
The closest solution I can come up with looks something like this:
class SomeInnerComponent {
var delegate: SomeProtocol?
}
protocol SomeProtocol {
func a0()
func a1()
}
class MyClass {
var someComponent = SomeInnerComponent()
var count = 0
struct Helper: SomeProtocol {
var dec: () -> ()
var inc: () -> ()
func a0() { dec() }
func a1() { inc() }
}
init() {
someComponent.delegate = Helper(
dec: { self.count -= 1 }, // see note below
inc: { self.count += 1 }
)
}
}
How it works:
Helper is an inner struct (could be a class, but a struct is simpler)
It implements the a0 and a1 methods, satisfying the requirements of SomeProtocol
The implementations of a0 and a1 call through to the closures dec and inc, which are stored properties (aka instance variables) of the Helper struct
You write (or otherwise specify) these closures when you construct a Helper instance (using the default member-wise initializer, Helper(dec: (Void -> Void), inc: (Void -> Void)))
Because you can write the closures when initializing a Helper, those closures can capture variables where you're calling the initializer, including the implicit self that refers to the MyClass instance creating the Helper.
You need both a0/a1 and dec/inc because you need closures (the latter), not methods, for capturing the enclosing state. And even though closures and funcs/methods are in many ways interchangeable, you can't create a method/func implementation by assigning a closure to a method/func name. (It'd be a different story if SomeProtocol required closure properties instead of methods, but I'm assuming SomeProtocol isn't something under your control.)
Anyway, this is kind of a lot of boilerplate and a layer of abstraction that you might not really need, so it's probably worth looking into other ways to architect your code.
Note: my example uses the closure { self.count -= 1 } where you might expect { self.count-- }. The latter doesn't work because that's an expression with a value, so Swift will interpret it as shorthand for the closure's return value. Then it'll complain that you assigned a () -> Int closure to a property that expects a () -> () (aka Void -> Void) closure. Using -= 1 instead works around this issue.
I would go for a different approach, I know this a pretty old topic but just in case someone else struggles with this issue:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
init(){
// Assign the delegate or do it somewhere else to your preference:
someComponenet.delegate = ProtocolImplementation(myClass: self);
}
private class ProtocolImplementation: SomeProtocol {
let selfReference: MyClass
init(myClass: MyClass){
selfReference = myClass
}
public func a0(){
selfReference.count--
}
public func a1(){
selfReference.count++
}
}
}
protocol SomeProtocol {
func a0()
func a1()
}
By following this approach it's also possible to include the same protocol multiple times, lets say your Protocol supports a generic and you want to implement it twice. SomeProtocol< SomeObject > and SomeProtocol< OtherObject > could be both used this way if needed.
Kind regards

What does Cannot create delegate without target for instance method or closure mean

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;
}
}

Resources