For example, I have this function parameter:
class MyClass {
U Function<T, U>(T data) callback;
MyClass ({ this.callback }) : super();
}
var int Function(String value) func = (String value) => int.parse(value);
MyClass(callback: func); // error
The error is:
The argument type 'int Function(String)' can't be assigned to the
parameter type 'U Function<T, U>(T)'.
How can I make this work?
EDIT:
Based on Shubhamhackz's answer, I conclude that the only thing wrong with my code is that because the generics are on the variables and not on the function parameters, and the variables are created when the class is declared and instantiated. I should put the <T, U> on the class declaration itself, and not on the function variable declaration. So the class declaration becomes like this:
class MyClass<T, U> {
U Function(T data) callback;
MyClass ({ this.callback }) : super();
}
To declare a generic function parameter and pass it as argument. This is the way how I would implement it in dart.
typedef Callback<T,U> = U Function(T data);
void main() {
Callback<String,int> func = (String value) {
return int.parse(value);
};
MyClass(callback: func);
}
class MyClass {
Callback<String,int> callback;
MyClass ({ this.callback }) : super() {
print('MyClass Called');
}
}
Output :
MyClass Called
Related
can someone tell me if there's a way to use enhanced enums in const constructors? I tried both named constructors and factory constructors, with no luck:
enum MyEnum {
first('first-string'),
second('second-string');
final String string;
const MyEnum(this.string);
}
class MyClass {
final String input;
const MyClass({required this.input});
const MyClass.first({
this.input = MyEnum.first.string, //error
});
factory MyClass.second() {
return const MyClass(input: MyEnum.second.string); //error
}
}
Named constructor gives error: The default value of an optional parameter must be constant
Factory constructor gives error: A value of type 'Null' can't be assigned to a parameter of type 'String' in a const constructor. Try using a subtype, or removing the keyword 'const'
Right now the only solution I found was to replace enum with a class containing static const params, like this:
class MyEnumClass {
static const String first = 'first-string';
static const String second = 'second-string';
}
You can use enums if you use the enum type itself for the member.
enum MyEnum {
first('first-string'),
second('second-string');
final String string;
const MyEnum(this.string);
}
class MyClass {
final MyEnum _input;
String get input => _input.string;
const MyClass({required MyEnum input}) : _input = input;
const MyClass.first({
MyEnum input = MyEnum.first,
}) : _input = input;
factory MyClass.second() {
return const MyClass(input: MyEnum.second);
}
}
In general, getters are equivalent to function calls and aren't constant expressions. (There are a some exceptions, and maybe enum members should be too.)
How do I null check or create a null safe block in Flutter?
Here is an example:
class Dog {
final List<String>? breeds;
Dog(this.breeds);
}
void handleDog(Dog dog) {
printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
}
void printBreeds(List<String> breeds) {
breeds.forEach((breed) {
print(breed);
});
}
If you try to surround it with an if case you get the same error:
void handleDog(Dog dog){
if(dog.breeds != null) {
printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
}
}
If you create a new property and then null check it it works, but it becomes bothersome to create new properties each time you want to null check:
void handleDog(Dog dog) {
final List<String>? breeds = dog.breeds;
if (breeds != null) {
printBreeds(breeds); // OK!
}
}
Is there a better way to do this?
Like the ?.let{} syntax in kotlin?
To get something similar to Kotlins .let{} i created the following generic extension :
extension NullSafeBlock<T> on T? {
void let(Function(T it) runnable) {
final instance = this;
if (instance != null) {
runnable(instance);
}
}
}
And it can be used like this:
void handleDog(Dog dog) {
dog.breeds?.let((it) => printBreeds(it));
}
"it" inside the let function will never be null at runtime.
Thanks to all the suggestions, but they were all some variation of moving the null check further down the code execution cain, which was not what i was looking for.
Yes, you'll have create a local variable just like you did to handle those things because if you don't create a local variable then if there is a class which is extending the Dog class can override breeds which will then become nullable even after you had checked it in the first place.
The other solution you can try is changing the List<String> to nullable in printBreeds method.
void handleDog(Dog dog) {
printBreeds(dog.breeds);
}
void printBreeds(List<String>? breeds) {
breeds?.forEach((breed) {
print(breed);
});
}
This error is right //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
as null type list is passing to function which says it accepts a non-null list
By below way breeds can be accessible
void printBreeds(List<String>? breeds) {
breeds?.forEach((breed) {
print(breed);
});
}
Also, if we don't want nullable operation every time, we can handle it while calling
Example:
class Dog {
final List<String>? breeds;
Dog(this.breeds);
}
void handleDog(Dog dog) {
print("handleDog");
printBreeds(dog.breeds!);
}
// This method only called if breeds not null
void printBreeds(List<String> breeds) {
print("printBreeds");
breeds.forEach((breed) {
print(breed);
});
}
void main() {
var dog = Dog(null);
handleDog(dog);
}
Output:
printBreeds
Pasting the following into dartPad.dev gives the error for the assignment classtwoInstance.fieldOne = arg;
Error: A value of type 'bool' can't be assigned to a variable of type 'bool'
Casting to a bool also gives an 'unnecessary cast' message
How do I assign the bool argument arg to fieldOne?
class ClassOne {
Function<T>(T) setFunction;
ClassOne({this.setFunction});
}
class ClassTwo {
bool fieldOne;
}
testFunction<T>(T value){
ClassTwo classtwoInstance = ClassTwo();
ClassOne classOneInstance = ClassOne(setFunction: <bool>(bool arg) {
classtwoInstance.fieldOne = arg; // Error A value of type 'bool' can't be assigned to a variable of type 'bool'
return null;
});
return classOneInstance;
}
void main() {
ClassOne classWithFunction = testFunction<bool>(true);
classWithFunction.setFunction(true);
}
Since you're using a generic type on your Function<T>(T) setFunction, the compiler gets confused when it's set to as bool in testFunction(). What you can do here is change the data type to generic and then cast arg with bool when setting to cassTwoInstance.fieldOne
Here's the updated code that you can try on dartpad.dev
class ClassOne {
Function<T>(T) setFunction;
ClassOne({required this.setFunction});
}
class ClassTwo {
bool? fieldOne;
}
testFunction<T>(T value) {
ClassTwo cassTwoInstance = ClassTwo();
ClassOne classOneInstance = ClassOne(setFunction: <T>(T arg) {
cassTwoInstance.fieldOne = arg as bool;
print('ClassTwo fieldOne: ${cassTwoInstance.fieldOne}');
return null;
});
return classOneInstance;
}
void main() {
ClassOne classWithFunction = testFunction<bool>(false);
classWithFunction.setFunction(false);
}
enum FooEnum: Int {
case fooEnumCase = 13512
case fooEnumCase2 = 425156
}
class FooClass {
public func myFunction(chosenEnumCase: fooEnum) -> String {
/* Logic */
}
}
fooClass.myFunction(fooEnum.fooEnumCase)
I am getting the error:
FooEnum is not convertible to FooClass
What is wrong with this code?
First of all the code is very hard to read with lowercase class names.
Two issues:
With this syntax the method is supposed to be a class function.
public static func myFunction(chosenEnumCase: FooEnum) -> String
Which raises a new error
Missing argument label 'chosenEnumCase:' in call
and you have to add the parameter label:
enum FooEnum: Int {
case fooEnumCase = 13512
case fooEnumCase2 = 425156
}
class FooClass {
public static func myFunction(chosenEnumCase: FooEnum) -> String {
//logic
}
}
FooClass.myFunction(chosenEnumCase: FooEnum.fooEnumCase)
To explain the error message let's consider fooClass.myFunction:
let f: (fooClass) -> ((fooEnum) -> String) = fooClass.myFunction
It's a function that expects an instance of fooClass, and returns another function ((fooEnum) -> String). Whereas in your code it is fed an instance of type fooEnum.
Call that function on a instance:
let myInstance = fooClass()
myInstance.myFunction(chosenEnumCase: fooEnum.fooEnumCase)
Or make myFunction a class function:
public class func myFunction(chosenEnumCase: fooEnum) -> String
PS: To conform to the Swift naming conventions use FooEnum and FooClass.
When creating a child class object which extends the generic base class, if it is created using convenience constructor, a callback contains the generic object will crash in the runtime because of the incorrect/corrupted object being passed back.
It is new in Swift3.1. There is no such problem in Swift 3.0.
It only happens when creating an object using convenience constructor.
protocol Initializable {
init()
}
class MyClass: Initializable {
required init() {}
func log() {
print("Hello")
}
}
class Parent<T: Initializable> {
typealias Callback = (T) -> Void
init(callback: Callback) {
callback(T())
}
}
class Child: Parent<MyClass> {
convenience init(value: Int, callback: Callback) {
self.init(callback: callback)
}
override init(callback: Callback) {
super.init(callback: callback)
}
}
let _ = Child { data in
data.log() //This is OK
}
let _ = Child(value: 0) { data in
data.log() //This will crash under swift3.1
}
Is it a language bug? Or am I doing something not supposed to be?