I need to implement a solution using generics that implements 3 interfaces, but as far as I can tell, generics in dart only supports 1 upper bound?
I have a model that looks like this:
abstract class Category implements Built<Category, CategoryBuilder>, Identifiable, Mapable {
...
}
The contents of the 3 interfaces is not really relevant, and what I'm trying to do, is construct a class that can process this in generic form.
What I want is something like this:
abstract class BaseDB<T extends Built<T, R> & Identifiable & Mapable, R extends Builder<T, R>> {
process(T entity) {
print(entity.id); // From Identifiable
entity.toMap(); // From Mapable
// ... etc
}
}
I know this is possible in both Typescript and Java, but I'm fairly new at Dart. Anyone know?
This is not possible in Dart. You can only put one bound on a type variable.
The bound of a Dart type variable is used to check which operations you can do on an object of the type parameter type. Example:
String something<T extends num>(T value) {
return value.abs().toString();
}
You are allowed to call abs() on value because we know that all instances of value are numbers, and num has an abs method.
If you can write <T extends Foo & Bar>, then there is no simple type in the Dart type system that can describe objects of type T. Dart does not have intersection types (the intersection type Foo & Bar would be a supertype of all types that are subtypes of both Foo and Bar, and a subtype of both Foo and Bar).
If Foo declares Baz method(), Bar declares Qux method(), and value has type T, what is the type of value.method()?
(It would either be disallowed, or the type would be Baz & Qux). This shows that allowing & in type variable bounds leaks intersection types into the remaining type system, and since Dart does not have intersection types, it also does not have multiple bounds on type variables.
When you declare a class, FooBar, implementing both Foo and Bar, you have the same issue: You need to figure out what method returns. However, the language requires you to write that solution into your class, to find some valid return type for FooBar.method, because otherwise the FooBar class declaration is not valid. It requires a user to find a solution to "find a subclass of both Baz and Qux".
Related
On dart tour page (https://dart.dev/guides/language/language-tour#getting-an-objects-type) these have a statement that testing variable type with "is" expression is more stable. Why is it so?
An is test is a subtype test.
If you do e is Iterable, you check whether the value of e implements Iterable<dynamic>, which includes any of List, Set and Queue, as well as all the subtypes of Iterable used internally by the runtime system, like _TakeIterable (which is used to implement Iterable.take).
It matches both values of type Iterable<Object> and Iterable<int>.
All of these are objects which can safely be used as an Iterable, and are intended to be used as such.
If you do e.runtimeType == Iterable, you are checking whether the Type object returned by e.runtimeType is equal to precisely the type Iterable<dynamic>. That will be false for any list, set or queue, for any actual iterable class which only implements Iterable, and even for something which returns the Type object of Iterable<int> or Iterable<Object?> from runtimeType.
I say that you check the object returned by e.runtimeType, not the run-time type of the value, because anyone can override the runtimeType getter.
I can make a class like:
class WFY {
Type get runtimeType => Iterable<int>;
}
void main() {
print(WFY().runtimeType == Iterable<int>); // True!
}
The value returned by runtimeType doesn't have to have any relation to the actual runtime type of the object.
Obviously it usually has, because there is no benefit in overriding runtimeType, because you shouldn't be using it for anything anyway,
Even if your code works today, say:
assert(C().runtimeType == C); // Trivial, right!
it might fail tomorrow if I decide to make C() a factory constructor which returns a subtype, _C implementing C.
That's a change that is usually considered non-breaking, because the _C class can do everything the C interface requires, other than having C as actual runtime type.
So, doing Type object checks is not stable.
Another reason using is is better than comparing Type objects for equality is that it allows promotion.
num x = 1;
if (x is int) {
print(x.toRadixString(16)); // toRadixString is on int, not on num
}
The is check is understod by the language, and trusted to actually guarantee that the value's runtime type implements the type you check against.
Comparing Type objects can mean anything, so the compiler can't use it for anything.
Some people like to use runtimeType in their implementation of ==, like;
class MyClass {
// ...
bool operator ==(Object other) =>
MyClass == other.runtimeType && other is MyClass && this.x == other.x;
}
This is intended to avoid subclass instance being equal to super-class instances when you ask the superclass, but not if you ask the subclass (the "ColorPoint problem", where ColorPoint extends Point with a color, and is equal to a another ColorPoint with the same coordinates and color, but if you ask a plain Point whether it's equal to a ColorPoint, it only checks the coordinates.)
This use of runtimeType "works", but is not without issues.
It means you cannot use mocks for testing.
It means you cannot create a subclass which doesn't extend the state, only the behavior, and which would want to be equal to the superclass instances with the same state.
And it means you do extra work, because you still need to cast the other object from Object to the surrounding type in order to access members, and Type object checks do not promote.
If possible, it's better to never allow subclasss of a concrete class that has a == method, and if you need to share other behavior, inherit that from a shared superclass.
(In other words: Don't extend classes that aren't intended to be extended, don't put == on classes which are intended to be extended.)
I have a factory class that is generic over T extends SomeClass<A>. But the thing is that A isn't known at the class level but only at the method level. To make myself a bit clearer with some code:
class Factory<T extends SomeClass<A>> { // Don't know A here
T<A> Function<A>(A arg) _constructor; // Function that produces my T's and is generic over A
Factory(this._constructor);
T<String> newString(String arg){ // A is only known here
return _constructor(arg);
}
T<int> newInt(int arg){
return _constructor(arg);
}
}
This obviously isn't legal Dart code, but is something to that effect even possible with Dart's generics, or does it require code generation? I tried to use extension methods, but they don't solve the problem that _constructor has the return type T<A>. And I explicitly don't want to / can't use T constructor<A, T extends SomeClass<A>>(A args).
Edit: I think what I was actually asking for is higher kinded types, which categorically aren't possible in Dart, and there is ongoing discussion on this matter (https://github.com/dart-lang/language/issues/1655). Excuse me if my understanding of the matter is incorrect.
That's not directly possible the way Dart currently works.
Dart's generics are only first order, so you cannot pass what is essentially a function from type to type as a type argument. Only plain types can be type arguments.
What you have here is a generic factory class. The kind of object it creates is defined at the class level (for ease, let's just assume that SomeClass here is Iterable, so it's a collection factory class, and you can choose, e.g., List or Set or Queue as the kind of collection to create), and then the element type of those collections are chosen when you call the factory methods.
That cannot work, because there is no type that the class can store in the class type argument which can allow that usage later.
I'd probably use separate classes and normal inheritance for this:
abstract class Factory {
Iterable<T> Function<T>(T) _constructor;
Factory(Iterable<T> Function<T>(T) constructor)
: _constructor = constructor;
Iterable<String> newString(String arg) => _constructor<String>(arg);
Iterable<int> newINt(int arg) => _constructor<int>(arg);
}
class ListFactory extends Factory {
ListFactory(List<T> Function<T>(T) constructor) : super(constructor);
List<String> newString(String arg) =>
super.newString(arg) as List<String>;
List<int> newInt(int arg) =>
super.newInt(arg) as List<int>;
}
Say I have the abstract class A
abstract class A {
A.someConstructor(Foo foo);
}
and all subclasses of A should then implement such constructor:
class B extends A {
#override
B.someConstructor(Foo foo) {
// ...
}
}
So basically what I want is some kind of abstract constructors.
Is there any way of achieving this (of course the above code does not work) or do I need a normal abstract method which then creates the object and sets its properties?
EDIT: Ok so it looks like the only way to create at least a similar behaviour would be something like this:
abstract class A {
A.someConstructor(Object foo);
}
class B extends A {
B.someConstructor(Object foo) : super.someConstructor(foo) {
// ...
}
}
This isn't exactly useful, and after some thinking about my problem I realized that in fact my original goal itself is not really neccessary, so this questions is now answered.
You want to enforce a pattern on the constructors of subclasses. The Dart language has no support for doing that.
Dart has types and interfaces which can be used to restrict values and class instance members.
If a class implements an interface, then its instance members must satisfy the signatures declared by the super-interface. This restricts instance members.
If a variable has a type, for example a function type, then you can only assign values of that type to it. This restricts values. Because a class is a subtype of its interfaces, the subclass restriction means that class typed variables can be used safely (the subtype can be used as its supertype because it has a compatible interface).
There is no way to restrict static members or constructors of classes, or members of libraries, because there is no way to abstract over them. You always have to refer directly to them by their precise name, so there is no need for them to match a particular pattern.
(Which may explain why you found the goal not necessary too).
In this situation, your subclasses must call the A.someConstructor constructor, but they are free to choose the signature of their own constructors. They can do:
class B extends A {
B.someConstructor(Object foo) : super.someConstructor(foo);
}
// or
class C extends A {
C.differentName(Object foo) : super.someConstructor(foo);
}
// or even
class D extends A {
D() : super.someConstructor(new Object());
}
Constructors aren’t inherited
Subclasses don’t inherit constructors from their superclass. A
subclass that declares no constructors has only the default (no
argument, no name) constructor.
Source
A common question, specifically since Dart 2, is if it is possible to require some or all generic type arguments on some or all types - for example List<int> instead of List or MyType<Foo> instead of MyType.
It's not always clear what the intention is though - i.e. is this a matter of style (you/your team likes to see the types), to prevent bugs (omitting type arguments seems to cause more bugs for you/your team), or as a matter of contract (your library expects a type argument).
For example, on dart-misc, a user writes:
Basically, if I have this:
abstract class Mixin<T> {}
I don't have to specify the type:
// Works class Cls extends Object with Mixin<int> {} // ...also works
class Cls extends Object with Mixin {}
Is there some way to make the second one not allowed?
Strictly speaking, yes, and no.
If you want to enforce that type arguments are always used in your own projects (instead of relying on type inference or defaults), you can use optional linter rules such as always_specify_types. Do note this rule violates the official Dart style guide's recommendation of AVOID redundant type arguments on generic invocations in many cases.
If you want to enforce that generic type arguments are always used when the default would be confusing - such as List implicitly meaning List<dynamic>, no such lint exists yet - though we plan on adding this as a mode of the analyzer: https://github.com/dart-lang/sdk/issues/33119.
Both of the above recommendations will help yourself, but if you are creating a library for others to use, you might be asking if you can require a type argument to use your class. For example, from above:
abstract class Mixin<T> {}
abstract class Class extends Object with Mixin {}
The first thing you could do is add a default bounds to T:
// If T is omitted/not inferred, it defaults to num, not dynamic.
abstract class Mixin<T extends num> {}
If you want to allow anything but want to make it difficult to use your class/mixin when T is dynamic you could choose a different default bound, for example Object, or even better I recommend void:
In practice, I use void to mean “anything and I don’t care about the elements”
abstract class Mixin<T extends void> {
T value;
}
class Class extends Mixin {}
void main() {
var c = Class();
// Compile-time error: 'oops' isn't defined for the class 'void'.
c.value.oops();
}
(You could also use Object for this purpose)
If this is a class under your control, you could add an assertion that prevents the class from being used in a way you don't support or expect. For example:
class AlwaysSpecifyType<T> {
AlwaysSpecifyType() {
assert(T != dynamic);
}
}
Finally, you could write a custom lint or tool to disallow certain generic type arguments from being omitted, but that is likely the most amount of work, and if any of the previous approaches work for you, I'd strongly recommend those!
I'm coming from C#/F#/Haskell so I'm trying to come up with solutions to programming problems I'm used to solving with types.
class A where T : new() in C#, this is mainly so I can do new T() somewhere. This creates a malformed type error in Dart. Is there a reasonably idiomatic way to solve this? Perhaps with factories?
I did some experiments with mixins, in the case of name conflicts for inherited mixin members, the last mixin wins. So for the following:
abstract class mixA { void foo(); }
abstract class mixB { void foo(); }
class C extends Object with mixA, mixB {}
new C().foo();
this would end up calling mixB.foo() whereas
class C extends Object with mixB, mixA {}
would end up calling mixA.foo()
Is there anyway to access the members of the hidden mixins?
Suppose I mix 2 mixins with a field of the same name. Does the subclass instance have 2 fields at runtime (just 1 is inaccessible) or is the object like a dictionary and there is only 1 slot for each name?
1 is not possible. You can't call new on a generic type (or variable for that matter). The most common workaround is to create a closure that allocates the object instead.
The answers for 2 fall out of the fact that Mixins can be seen as super-classes: A extends Object with B, C is basically equivalent to:
class B' extends Object { <copy over code inside B> }
class C' extends B' { <copy over code inside C> }
class D extends C' { ... }
With this in mind:
no. there is no way to access hidden super elements.
yes. you would end up with multiple fields.
Small note: the <copy over code inside X> part is not completely correct since that would change the library-scope. Code from mixin B is conceptually in the library the mixin is declared in.