I have an abstract base class validator with a method which takes a generic type as parameter.
I will be passing generic type parameter to base class from the subclass inheriting the base class.
Base Class:
abstract class BaseValidator {
bool isValid<T>(T obj);
}
Child Class:
class IPv4Validator extends BaseValidator{
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid<String>(String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}
Here hasMatch takes in non nullable string. When I directly pass some string hasMatch doesn't throw an error.
But when I try to pass the generic value in the method parameter, it shows an error.
The argument type 'String' can't be assigned to the parameter type
'String'.
I couldn't able to understand why generic type is not accepting, even though its compile-time type.
The following code solves this particular problem. But it may be different from what you intended to implement. On the other hand, the code will be cleaner if you create a new concrete class for different data types.
abstract class BaseValidator<T> {
bool isValid(T obj);
}
class IPv4Validator extends BaseValidator<String>{
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid(String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}
Explanation.
In the line class IPv4Validator extends BaseValidator<String> we are not declaring a new class BaseValidator, it is already declared as BaseValidator<T>. Here we are inheriting the specialization of the existing generic class BaseValidator. While in the line bool isValid<String>(String obj), we declare a new function, so the compiler understands it as if we were declaring a new generic function with a parameter type named String. So, here bool isValid<String>(String obj) is equivalent to bool isValid<T>(T obj), just instead of name T we used name String, which is not an object String.
another fix that you can do is to use the covariant keyword, to implement that, try this:
abstract class BaseValidator<T> {
bool isValid(T obj);
}
class IPv4Validator extends BaseValidator {
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid(covariant String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}
I have defined a class like this and annotated with the freezed library.
#freezed
#immutable
abstract class CommentMediaAttachmentModel with _$CommentMediaAttachmentModel {
const factory CommentMediaAttachmentModel({
final String type,
final String mediaUrl,
final int width,
final int height
}) = _CommentMediaAttachmentModel;
bool isAnimated() {
return type == 'ANIMATED';
}
}
I'd like to add a quick function isAnimated to determine the type variable, but on compilation, it doesn't allow me to do so:
lib/presentation/comment/model/comment_attachment_model.freezed.dart:292:7: Error: The non-abstract class '_$_CommentMediaAttachmentModel' is missing implementations for these members:
- CommentMediaAttachmentModel.isAnimated
Try to either
- provide an implementation,
- inherit an implementation from a superclass or mixin,
- mark the class as abstract, or
- provide a 'noSuchMethod' implementation.
Upon checking the generated class _$_CommentMediaAttachmentModel, isAnimated function isn't implemented. How can I achieve that?
Edit: Below is the code of _$_CommentMediaAttachmentModel.
I'm not sure why I cannot paste that snippet to SO, it just said the code is malformed. I will use a screen capture instead:
To manually define methods/properties on the class, as stated in freezed documentation, you have to define a single private constructor:
#freezed
#immutable
abstract class CommentMediaAttachmentModel with _$CommentMediaAttachmentModel {
const CommentMediaAttachmentModel._(); // Added constructor
const factory CommentMediaAttachmentModel({
final String type,
final String mediaUrl,
final int width,
final int height
}) = _CommentMediaAttachmentModel;
bool isAnimated() {
return type == 'ANIMATED';
}
}
Basically You need to implement isAnimated inside the _$CommentMediaAttachmentModel too. because it is a mixin of the abstract class.
hi I am new with bloc in flutter and I'm trying to understand block timer In the doc of flutter_bloc and I would know what's this constructor class mean
#immutable
abstract class TimerState extends Equatable {
final int duration;
//and this list props ??
TimerState(this.duration, [List props = const []])
: super([duration]..addAll(props));
}
Update: For the new version of Equitable package >= v 0.6.0 read my article on Medium, for older version or deep understanding read this answer.
When your father gives you and your brother 2 gifts, both gifts are laptops but they are not the same type of laptops; you want to know are both gifts equal or not! So you will compare all aspects that important to you RAM, SSD, CPU.
on a paper: myLaptop: 16G/256G/i5 | myBrotherLaptop: 8G/512G/i5
Assuming your brain is using Dart language, and you thought of each gift as an object of this class:
class LaptopGiftClass {
int ram;
int ssd;
String cpu;
// Default constructor
LaptopGiftClass(this.ram, this.ssd, this.cpu);
}
then to compare Equailty of both gifts that created using the class above, Dart and other Object oriented languages e.g Java, C# are expecting you to create(override) these functions, in order to make these languages understand the objects and able to compare any two objects of same class:
#override
bool operator ==(Object myBrotherLaptop) =>
identical(myLaptop, myBrotherLaptop) ||
myBrotherLaptop is LaptopGiftClass &&
runtimeType == myBrotherLaptop.runtimeType &&
name == myBrotherLaptop.name;
#override
int get hashCode => name.hashCode;
if these lines scares you off, no one blame you, that's why nice people have created equatable package for us!
Equatable package is telling you "leave this scary job for me" But how to delegate the scary code to equatable package??!
By doing two things:
Make your class extends equatable:
dart class LaptopGiftClass extends Equatable {...}
Pass all properties that you need to compare with inside an array to the Equatable(the super/parent class) from moment one, so inside the constructor:
LaptopGiftClass(this.ram, this.ssd, this.cpu) : super([ram, ssd, cpu]);
your final class is:
class LaptopGiftClass extends Equatable {
int ram;
int ssd;
String cpu;
// Default constructor
LaptopGiftClass(this.ram, this.ssd, this.cpu) : super([ram, ssd, cpu]);
}
AND YOU ARE DONE! you can now check equality of the two gifts, just create the objects then compare:
LaptopGiftClass myLaptop = LaptopGiftClass(16,256,'i5');
LaptopGiftClass myBrotherLaptop = LaptopGiftClass(8, 512,'i5');
AND JUST BEFORE START COMPARING, your brother saw you, and because he is a gamer, he wants you to add more properties in this equality check: GPU and Screen_Resolution! your mother heard that and asked you to add price too!
Now you have a list of new props to compare: [GPU, Screen_Resolution, Price].
So because you follow clean code principle, you expected that, and you made the constructor able to get more properties to compare with:
// This only mean combine both lists
[ram, ssd, cpu]..addAll(myBrotherAndMotherProps)
so your final class is:
class LaptopGiftClass extends Equatable {
int ram;
int ssd;
String cpu;
// Default constructor
LaptopGiftClass(
this.ram,
this.ssd,
this.cpu,
// List of list => because we think "clean code"
// and maybe in the future we will send other data; NOT
// only an array(list)..
// so we here sent the extra props we need to
// compare 'myBrotherAndMotherProps', and
// as sometime brother and mother will not ask you
// to add props to compare, you give it a default value
// as empty "const []", why const here??! just for better
// performance as we are so soooo Professional!!
[ List myBrotherAndMotherProps = const [] ],
) : super([ram, ssd, cpu]..addAll(myBrotherAndMotherProps));
// WHY TO PASS FROM INSIDE THE CONSTRUCTOR?
// because Equatable needs them (required)
// and not at anytime but immediately inside the
// constructor of itself, so we made this
// chaining(constructor pass to another constructor)..
}
So it's obvious that the essintial properties are [RAM, SSD, CPU], but anything extra will be take into consideration too as we made the implementation clean, flexible, and scalable.
before adding this flexible code List<Object> get props => [RAM, SSD, CPU]..addAll(myBrotherAndMotherProps); these used to be EQUAL!!:
// Note first 3 are equal [ram, ssd, cpu]:
LaptopGiftClass myLaptop = LaptopGiftClass(16,256,'i5', ['Nvidia', 1080, '1200$']);
LaptopGiftClass myBrotherLaptop = LaptopGiftClass(16, 256,'i5', ['Intel HD', 720, '900$']);
myLaptop == myBrotherLaptop; // True without ..addAll(myBrotherAndMotherProps);
myLaptop == myBrotherLaptop; // False with ..addAll(myBrotherAndMotherProps);
Same happening with TimerState:
#immutable
abstract class TimerState extends Equatable {
final int duration;
TimerState(this.duration, [List props = const []])
: super([duration]..addAll(props));
}
TimerState is implemented just like LaptopGiftClass above(last implementation).
you can send props to it using the constructor:
TimerState(this.duration, [List props = const []])
: super([duration]..addAll(props));
so TimerState will pass props's list to its parent(super/ the Equatable/ what extended..) in this line like this:
: super([duration]..addAll(props));
and in this timer example; duration is the basic prop, just like [RAM, SSD, CPU] to LaptopGiftClass.
and the hierarchy will be like this:
// Inside class Paused extends TimerState {...}
Paused(int duration) : super(duration); // super is TimerState
// then Inside abstract class TimerState extends Equatable {..}
TimerState(this.duration, [List props = const []])
: super([duration]..addAll(props)); // super is Equatable
// Then Equatable will get props and deal with it for you...
This tutorial use equatable: ^0.2.0, in this version when you want to override the hashcode and == operator you need to pass a List of the properties to the super constructor. Check out the docs.
Whit this in mind, he create a optional parameter called props, and pass to the super constructor a List that contains duration and all elements of the props parameter.
abstract class TimerState extends Equatable {
final int duration;
TimerState(this.duration, [List props = const []])
: super([duration]..addAll(props));
}
In this way the class that extends TimerState can use the props optional parameter to pass other properties, and this properties would be added to the List that is passed to the super constructor of TimerState for use Equatable correctly.
So if you need a state that have other properties you need to do this:
class OtherTimerState extends TimerState {
final int otherProperty1;
final int otherProperty2;
OtherTimerState(int duration, this.otherProperty1, this.otherProperty2)
: super(duration, [otherProperty1, otherProperty2]);
}
I have API communication service in my Flutter app with 10+ different services, and 100+ API calls that heed to parse data. In order to reuse code I've decided to create some common parsing code that is going to parse data from API:
ApiResponse handleObjectResponse({
#required http.Response serverResponse,
#required Function objectConstructor,
}) {
if (serverResponse.statusCode == 200) {
dynamic responseObject = objectConstructor(json.decode(serverResponse.body));
return ApiResponse(responseObject: responseObject);
} else {
ApiError error = responseHasError(serverResponse.body);
return ApiResponse(error: error);
}
}
This way I am able to parse JSON object from API in a reusable way no matter what the Object class is, just by passing constructor function to this method.
When I call this method in any of the Services I've created for fetching data like this:
handleObjectResponse(serverResponse: response, objectConstructor: ChartData.fromJson);
I get error: The getter 'fromJson' isn't defined for the class 'ChartData'.
Try importing the library that defines 'fromJson', correcting the name to the name of an existing getter, or defining a getter or field named 'fromJson'.
Where I think the problem is is in this model class and factory statement, but I don't know how to fix it:
class ChartData {
List<ChartDataPoint> points;
ChartData({
this.points,
});
factory ChartData.fromJson(Map<String, dynamic> json) {
List jsonPoints = json["data"];
return ChartData(
points: List.generate(jsonPoints.length,
(i) => ChartDataPoint.fromJsonArray(jsonPoints[i])));
}
}
You cannot pass constructors as functions. You need to create a function what will call the constructor instead:
(int a) => Foo(a);
Just a 2022 update: since 2.15 it's possible by Class.new, see the complete issue: https://github.com/dart-lang/language/issues/216.
class A {
final String a;
const A(this.a);
#override
String toString() => 'A($a)';
}
class B {
final String b;
const B(this.b);
#override
String toString() => 'B($b)';
}
void main() {
final List<Object Function(String)> constructors = [A.new, B.new];
for (final Object Function(String) constructor in constructors) {
final Object instance = constructor('My Constructor Parameter');
if (instance is A) {
print(instance.toString());
}
}
}
Note that if you're using named params, both class constructors must have the same param name, otherwise the constructor signatures won't match and then it will generate this static error:
The element type X can't be assigned to the list type Y.
I have an abstract class Model with a static attribute and another generic class Controller<T extends Model>. I want to access the static attribute of Model in an instance of Controller. That should like this:
abstract class Model{
static hasStatus: boolean = false;
}
class MyModel extends Model{
static hasStatus = true;
}
class Controller<T extends Model>{
constructor(){
if(T.hasStatus)...
}
}
But TS says 'T' only refers to a type, but is being used as a value here.
Is there an easy way to achieve this? Or should i subclass Controller for each Heritage of Model and implement a method to retrieve the value?
There is no way to do that in typescript. Generic type parameters can only appear where types may appear in declarations, they are not accessible at runtime. The reason for that is simple - single javascript function is generated for each method of the generic class, and there is no way for that function to know which actual type was passed as generic type parameter.
If you need that information at runtime, you have to add a parameter to the constructor and pass a type yourself when calling it:
class Controller<T extends Model>{
constructor(cls: typeof Model){
if (cls.hasStatus) {
}
}
}
let c = new Controller<MyModel>(MyModel);
Here is how it looks when compiled to javascript to illustrate the point - there is nothing left of generic parameters there, and if you remove cls parameter there is no information about where hasStatus should come from.
var Controller = (function () {
function Controller(cls) {
if (cls.hasStatus) {
}
}
return Controller;
}());
var c = new Controller(MyModel);