I am trying to create a class "CombinedResource" that will contain 2 properties, each is of the type "Resource".
Now the Resource class is as follows:
class Resource<T>{
final T? data;
Resource({this.data});
}
And the CombinedResource class is as follows:
class CombinedResource<T1 extends Resource<Ta>, T2 extends Resource<Tb>, Ta, Tb>
{
final T1? resourceA;
final T2? resourceB;
const CombinedResource._({
this.resourceA,
this.resourceB,
});
factory CombinedResource.create(
Resource<Ta> rA,
Resource<Tb> rB,
) =>
CombinedResource._(
resourceA: rA,
resourceB: rB,
);
}
Now, the problem is that the compiler is giving me an error on the factory method:
The argument type 'Resource<Ta>' can't be assigned to the parameter type 'T1?'.
I tried changing the CombinedResource class to be as follows:
class CombinedResource<Ta,Tb>
{
final Resource<Ta>? resourceA;
final Resource<Tb>? resourceB;
const CombinedResource._({
this.resourceA,
this.resourceB,
});
factory CombinedResource.create(
Resource<Ta> rA,
Resource<Tb> rB,
) =>
CombinedResource._(
resourceA: rA,
resourceB: rB,
);
}
This caused another problem, where I was unable to create an instance of CombinedResource:
CombinedResource.create(
Resource(data:"sdc"), Resource(data:"sdc"));
The argument type 'String' can't be assigned to the parameter type 'Resource<String>?'.
What am I doing wrong here?
Update:
The second approach is working fine.
Your first attempt failed because you do:
factory CombinedResource.create(
Resource<Ta> rA,
Resource<Tb> rB,
) =>
CombinedResource._(
resourceA: rA,
resourceB: rB,
);
but CombinedResource._ expects arguments of type T1 and T2. You've specified that T1 is derived from Resource<Ta>, which means that an instance of a T1 must be an instance of a Resource<Ta>. The reverse, however, is not true: not every Resource<Ta> is necessarily a T1. Therefore a Resource<Ta> is not implicitly assignable to a T1. (Downcasts are potentially unsafe.)
I don't know why you inexplicably made your factory constructor use a different signature from your private constructor, but it'd work if you fixed it to use matching types:
factory CombinedResource.create(
T1 rA,
T2 rB,
) =>
CombinedResource._(
resourceA: rA,
resourceB: rB,
);
Related
Minimum reproducible code:
class Parent {}
class Child extends Parent {}
class Foo<T extends Parent> {
final T t;
Foo(this.t);
Foo.one(Child child) : this(child); // Compile error
}
The argument type 'Child' can't be assigned to the parameter type 'T'.
Why can't I pass Child in redirecting constructor, in other words, why doesn't Dart know that Child satisfies T extends Parent relationship? Though I can do
void main() => Foo(Child());
Note: I'm looking for a reason as to why I can't do that. Please don't post answers to use this(child as T).
Because the code is not valid since you could do:
class Monster extends Parent {}
void main() {
final foo = Foo<Monster>.one(Child());
}
Which is a problem since Monster does extend from Parent but the constructor argument takes a Child and will then try assign this to T which is Monster.
If we do the change you don't want:
Foo.one(Child child) : this(child as T);
Then we will get the error:
type 'Child' is not a subtype of type 'Monster' in type cast
#0 new Foo.one (./bin/example1.dart:8:37)
#1 main (./bin/example1.dart:14:15)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
So you cannot say that Child can always be assigned to a variable T where T extends Parent.
I have a class:
class Foo<T> {
final void Function(T) bar;
Foo(T t, {required this.bar});
}
and I'm passing int value so that T can be inferred as int, but the issue is in the following code, t is of type Object? and not int. Why is that so?
Foo(0, bar: (t) {
// `t` is of type `Object?`
});
Note: I'm not looking for a solution, which is to use Foo<int>(0, bar: ...). I want to know the reason why t is not inferred correctly
The situation you describe should now be fixed as of Dart 2.18.
Original answer for earlier versions of Dart
Your problem can be more clearly observed with:
class Foo<T> {
final void Function(T) bar;
Foo(T t, {required this.bar});
}
Type staticType<T>(T object) => T;
void main() {
var foo = Foo(0, bar: (x) => print(staticType(x)));
print(staticType(foo)); // Prints: Foo<int>
print(staticType(foo.bar)); // Prints: (int) => void
print(foo.bar.runtimeType); // Prints: (Object?) => void
foo.bar(42); // Prints: Object?
}
I'm not an expert on the inference rules, but (prior to Dart 2.18) inference either could flow top-down (from function to arguments) or bottom-up (from arguments to function). Since you don't supply an explicit type parameter when constructing Foo, Foo's generic type argument T must be inferred from its arguments. However, the argument type to the anonymous function also isn't specified, so it's assumed to be Object?.
For Foo's T to be inferred as int and for the anonymous function to be inferred to be (int) => void would require inference to flow from the positional argument up to the function and back down to the named argument, which Dart (prior to 2.18) did not do. You thus instead ended up with the (Object?) => void anonymous function being implicitly cast to (int) => void.
What I'm trying to do
Given the following Node:
class Node<T> {
Node(this.value);
T value;
Node? child;
// TODO: add `visit` method
#override
String toString() => value.toString();
}
I'd like to add a visit method that will perform some action on the value of each node and its child recursively. Then I could do something like this:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value));
// 1
// 2
// 3
}
Naive solution
If I do the following, it works:
void visit(Function action) {
action(value);
child?.visit(action);
}
Problems with the naive solution
However, the value in this statement is inferred to be dynamic:
root.visit((value) => print(value));
I'd like to infer it to be the same type as the Node's generic T type.
Additionally, the compiler allows the following, which causes a runtime crash:
root.visit(() => 42);
I'd like that to be a compile-time error.
Attempted solution 1
If I change visit to the following:
void visit(Function(T value) action) {
action(value);
child?.visit(action(value));
}
Everything looks good at compiletime:
root.visit(print); // OK
root.visit((value) => print(value)); // OK
root.visit(() => 42); // error
But if I comment out that last one and run the code on either of the first two then I'll get the following runtime error:
Unhandled exception:
type 'Null' is not a subtype of type '(dynamic) => dynamic'
I'm not exactly sure what that means.
Attempted solution 2
Added void:
void visit(void Function(T value) action) {
action(value);
child?.visit(action(value)); // error
}
This expression has a type of 'void' so its value can't be used.
Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void. (dartuse_of_void_result)
Attempted solution 3
This one was just a stab in the dark:
void visit(void Function<T>(T value) action) {
action(value);
child?.visit(action);
}
The visit method seems to compile but calling it as before gives compile time errors:
root.visit(print); // error
root.visit((value) => print(value)); // error
The errors read:
The argument type 'void Function(Object?)' can't be assigned to the parameter type 'void Function(T)'. (dartargument_type_not_assignable)
Related questions
These questions seem related but I couldn't figure out how to extract a solution from them:
How to create a generic method in Dart?
Callback with generic type parameter in Dart
Dart: Type error when overrding generic function in abstract class
How to check and cast generic parameters in Dart?
Dart passing generic Function<T>(T t) seems to require cast, all other ways signatures don't match
How can I solve the problem?
Thank you to #jamesdlin in the comments for solving the problem.
You need to set the generic type for the child as Node<T>. Then you can specify the method signature as void visit(Function(T value) action) and pass action itself on to the child.
Here is the full example:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value)); // value inferred as int
// root.visit(() => 42); // compile-time error
// 1
// 2
// 3
}
class Node<T> {
Node(this.value);
T value;
Node<T>? child;
void visit(Function(T value) action) {
action(value);
child?.visit(action);
}
#override
String toString() => value.toString();
}
This is the program:
import 'dart:collection';
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry> {
T value;
MyLinkedListEntry(T this.value);
#override
String toString() => '${super.toString()}: ${value}';
}
void main(List<String> args) {
var l = LinkedList<MyLinkedListEntry>();
var s = MyLinkedListEntry("SomeString");
var p = MyLinkedListEntry(125);
l.add(s);
s.insertAfter(p);
p.insertAfter(MyLinkedListEntry(126));
l.forEach((e) => print(e));
}
And it gives this output:
Instance of 'MyLinkedListEntry<String>': SomeString
Instance of 'MyLinkedListEntry<int>': 125
Instance of 'MyLinkedListEntry<dynamic>': 126
I expected the third instance to be of type LinkedList<int> as well. Why it's not?
This is with Dart 2.13.4.
0. dynamic in type checking
Everything is a subclass of dynamic:
print(1 is dynamic); // Outputs true
print("a" is dynamic); // Outputs true
In fact, Dart even shows a warning when using the above code: Unnecessary type check; the result is always 'true'.
1. Omiting type parameters in declarations
In the declaration
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry>
note that you're not passing the type parameter of MyLinkedListEntry in the type parameter of LinkedListEntry. From docs (emphasis mine):
When a generic class is instantiated without explicit type arguments, each type parameter defaults to its type bound [...] if one is explicitly given, or dynamic otherwise.
So Dart interprets this as
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry<dynamic>>
2. The extends clause in type parameters
Let's look at the declaration of LinkedListEntry:
abstract class LinkedListEntry<E extends LinkedListEntry<E>>
Note that LinkedListEntry requires a type parameter named E, which must be a subclass of LinkedListEntry. When you use LinkedListEntry<E>, E must extend LinkedListEntry<E>.
When you declare MyLinkedListEntry<T>, you're passing MyLinkedListEntry<dynamic> as E. Since T always extends from dynamic, MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry<dynamic>>, so this is a valid declaration.
3. Type parameters in methods
In the expression
p.insertAfter(MyLinkedListEntry(126));
you're using the insertAfter method declared in the LinkedListEntry class. Let's look at its declaration:
void insertAfter(E entry)
Since E is equal to MyLinkedListEntry<dynamic>, Dart will interpret any MyLinkedListEntry call to this method as
void insertAfter(MyLinkedListEntry<dynamic> entry)
Therefore, when you do
p.insertAfter(MyLinkedListEntry(126));
you're actually passing an upcasted MyLinkedListEntry<dynamic>, which explains the output.
The solution
Explicitly pass the type parameter of MyLinkedListEntry when inserting:
p.insertAfter(MyLinkedListEntry<int>(126));
I am attempting to construct an abstract class that requires a named constructor in Dart. Given some Map (m), this generic type must be able instantiate itself.
The Dart compiler is throwing T.fromJson -> Invalid constructor name.
My attempt at coding this:
abstract class JsonMap<T> {
Map toJson();
T.fromJson(Map m);
}
I struggled with the same concept (in the same place ... API parsing :)) ) and I didn't found a proper solution.
But maybe you can use something this thing I found while checking block pattern this (I am not using it for my model part):
abstract class SomeBase {
void load();
}
class Provider<T extends SomeBase> extends InheritedWidget {
final T something;
Provider({
Key key,
#required this.something,
}): super(key: key);
#override
bool updateShouldNotify(_) {
return true;
}
static Type _typeOf<T>() => T;
static T of<T extends SomeBase>(BuildContext context){
final type = _typeOf<Provider<T>>();
Provider<T> provider = context.inheritFromWidgetOfExactType(type);
return provider.something;
}
}
OR just use this without encapsulating it in an inherited widget and provide the already initialised objects (like user or whatever you are parsing) that just load the values from the JSON provided.
You're creating a class named JsonMap that is parameterized on type T. T is not the name of your class, so T.fromJson is not a valid named constructor for JsonMap.
If you want JsonMap to have a named constructor, it should be JsonMap.fromJson(Map m).
Untested, but off the top of my head, you should write your code like so:
abstract class JsonMap<T> {
Map<String, dynamic> toJson();
T fromJson(Map<String, dynamic> m);
}
The dot makes fromJson(Map m) a constructor of type T, or a static function belonging to type T. Without the dot, it is a function belonging to the abstract class JsonMap, returning type T. Specifying the map type is good practice if you know what it will be (like with json).