class Foo {
final int x;
Foo([this.x = defValue]); // Compile-time error
static get defValue => 10;
}
Error:
The default value of an optional parameter must be a constant.
defValue is a compile time constant, so I should be able to pass its value to the constructor.
The expression defValue is not a compile-time constant expression. Evaluating it requires executing the getter to get a value, and constant evaluation cannot execute getters or methods (except a very specific list of allowed platform methods, like int.operator+). It might be that the expression returned by executing the defValue getter is itself a compile-time constant expression, but it's being returned through a non-constant operation.
Change the defValue definition to
static const defValue = 10;
then it should work. Reading constant declarations is a compile-time constant operation.
You refer to the docs saying that you can use a static method as a compile-time constant. That is correct, but that's the method itself which is the constant, you are still not allowed to call it.
That is:
static int foo(int x) => x;
static const fooRef = foo; // valid!
works because referencing the foo function value is a constant expression.
A getter is not a method, and you cannot do a "tear-off" of the getter. When you refer to it, you need to execute its body, and that's not allowed in a constant context.
My guess is that defValue is not known at compile time (since it's a getter and not a constant), so you should either use a constant variable like static const int defValue = 10 or initialize the class like this:
class Foo {
final int x;
Foo([int x]) : this.x = x ?? defValue;
static get defValue => 10;
}
Related
In Kotlin I can do something like:
var myType : KClass<String>? = null
and can assign to it like:
myType = String::class
but NOT like:
myType = Int::class // Type mismatch: inferred type in KClass<Int> but KClass<String>? was expected
Is there something similar in Dart? I know of the Type type but it is not generic and while it can represent String or List<int> I seem not to be able to write similar code as my Kotlin example:
Type? t = null;
I can assign to it:
t = String;
AND also:
t = int;
but I want the second example to fail compilation. I would need some kind of Type<String>. Is this possible in Dart?
The Type class is not generic, and doesn't support subtype checks (or any other reasonable type-related operation). There is no way to use it for what you are trying to do.
So, don't. It's useless anyway. However, in Dart you can create your own type representation that is actually useful, because Dart doesn't erase type arguments, and you can then ask people using your code to ass that instead.
Say:
class MyType<T> implements Comparable<MyType>{ // Or a better name.
const MyType();
Type get type => T;
bool operator >=(MyType other) => other is MyType<T>;
bool operator <=(MyType other) => other >= this;
bool isInstance(Object? object) => object is T;
R runWith<R>(R Function<T>() action) => action<T>();
#override
int get hashCode => T.hashCode;
#override
bool operator==(Object other) => other is MyType && T == other.type;
}
With that you can write:
MyType<String?> type;
type = MyType<Null>(); // valid
type = MyType<String>(); // valid
type = MyType<Never>(); // valid
type = MyType<int>; // EEEK! compile-time error
You can use it where you need to store a type as a value.
The thing is, most of the time you can just use a type variable instead ,and creating an actual value to represent a type is overkill.
So, first try to just use a type parameter, instead of passing around Type or MyType objects. Only if that fails should you consider using MyType. Using Type is probably a mistake since it's not good for anything except doing == checks, which is antithetical to object orientation's idea of subtype subsumption.
I think this is the best you can get :
void main() {
aFunction<String>(String, '');
aFunction<String>(String, 1);
}
void aFunction<V>(Type type, V value) {
print(value.toString());
}
if you run this in a dartpad, you will see that
aFunction<String>(type, 1);
Doesn't compile.
But that's not really efficient because the type isn't guessed by Dart, you have to specify the generic type by hand.
I'm using Dart 2.17
Function foo = (int x) => x;
if (foo is Function(Object)) {
print('Bar'); // Doesn't print
}
According to me, Function(int) is a subtype of Function(Object) since type int is subtype of type Object and hence the if condition should return true but it doesn't. Why is that so?
I wasn't getting any good title for this question, feel free to change it.
It is not.
The point of subtypes is that an instance of a subtype can be used everywhere an instance of the supertype is expected (also known as "substitutability").
A Function(Object) can be called with any object as argument. It can be called with both "foo" and 42 as arguments.
A Function(int) cannot be called with a string as argument. That means that a Function(int) cannot be substituted where a Function(Object) is expected, and the type system does indeed not make Function(int) a subtype of Function(Object).
It does make Function(Object) a subtype of Function(int) instead, because if a function which can be called with any argument, then it can be used anywhere a function accepting an int argument is needed - because it accepts an int.
In general, a R1 Function(P1) function type is a subtype of R2 Function(P2) if R1 is a subtype of R2 and P2 is a subtype of P1. Notice the reverse ordering of the parameter type. Parameter types are covariant.
Think of it as: A function can be used in a place where it's given an argument of type P and is expected to return a value of type R if (and only if) it accepts any P argument and possibly more, and the values it might return must all be R values but not necessarily all R values.
That is, a function type is a subtype of R Function(P) if it accepts at least P and returns at most R.
Though it's not immediately intuitive, the subtyping is the opposite: Function(Object) is a subtype of Function(int). Indeed, a type A can be a subtype of B only if anything that holds about B, also holds about A. Function(Object) can always be called with an int argument, but Function(int) cannot be called with an arbitrary Object, so:
Function foo = (Object x) => x;
if (foo is Function(int)) {
print('Bar'); // prints!
}
In other words, functions are contravariant in the parameter type.
class X extends Y {
X(int a, int b) : super(a,b);
}
Can someone give me an explanation about the syntax meaning of the colon :?
This feature in Dart is called "initializer list".
It allows you to initialize fields of your class, make assertions and call the super constructor.
This means that it is not the same as the constructor body. As I said, you can only initialize variables and only access static members. You cannot call any (non-static) methods.
The benefit is that you can also initialize final variables, which you cannot do in the constructor body. You also have access to all parameters that are passed to the constructor, which you do not have when initializing the parameters directly in the parentheses.
Additionally, you can use class fields on the left-hand of an assignment with the same name as a parameter on the right-hand side that refers to a parameter. Dart will automatically use the class field on the left-hand side.
Here is an example:
class X {
final int number;
X(number) : number = number ?? 0;
}
The code above assigns the parameter named number to the final field this.number if it is non-null and otherwise it assigns 0. This means that the left-hand number of the assignment actually refers to this.number. Now, you can even make an assertion that will never fail (and is redundant because of that, but I want to explain how everything works together):
class X {
final int number;
X(number): number = number ?? 0, assert(number != null);
}
Learn more.
It's ok to access non static member in initializer list.
class Point {
num x, y;
Point(this.x, this.y);
Point.origin(): this.x = 10, this.y = 10;
}
main() {
Point p = Point.origin();
print(p.x); // 10
}
I know that const is compile-time constant in dart, but I don't understand mechanism behind const [F0, F1, F2] in the following code:
class Foo {
static const F0 = 'F0';
static const F1 = 'F1';
static const F2 = 'F2';
// const list of const values I guess...
static const CONST_LIST = const [F0, F1, F2]; // please explain this line
static final String FOO = CONST_LIST[0]; // ok
// compile error: 'const' varaibles must be constant value
// static const String BAR = CONST_LIST[1];
}
main() {
// is CONST_LIST const or not?
// below line it's ok for dartanalyzer but
// in runtime: Cannot change the content of an unmodifiable List
Foo.CONST_LIST[1] = 'new value';
}
I noticed that const is required by dart analyzer in const [F0, F1, F2]; but it does make list more like final (runtime immutable list) rather than compile time const.
UPDATE:
Another question is why CONST_LIST[1] is not "constant value". See commented declaration of Foo.BAR.
Günter has answered the second part of your question. Here is some more information about const.
Const means that the object's entire deep state can be determined entirely at compile time and that the object will be frozen and completely immutable.
More information in this article. Also see the following question.
Regarding the second part of your question, consider the following:
const int foo = 10 * 10;
The expression "10 * 10" can be evaluated at compile time, so it is a "constant expression". The types of things you can do in a constant expression need to be quite limited (otherwise you could run arbitrary Dart code in the compiler!). But some of these limitations are being relaxed as dart matures, as you can see in the bug which Günter linked to.
In contrast, consider the following:
final int bar = 10;
final int foo = bar * bar;
Since "bar * bar" is not a constant expression it is evaluated at runtime.
There is an open bug for this: see https://github.com/dart-lang/sdk/issues/3059
Having a simple class as follows is considered a static warning, why?
operator 'negate' should return numeric type
class Vector {
final int x,y;
const Vector(this.x, this.y);
Vector operator +(Vector v) { // Overrides + (a + b).
return new Vector(x + v.x, y + v.y);
}
Vector operator -(Vector v) { // Overrides - (a - b).
return new Vector(x - v.x, y - v.y);
}
Vector operator negate() { // Overrides unary negation (-a).
return new Vector(-x,-y);
}
String toString() => '($x,$y)';
}
main() {
final v = new Vector(2,3);
final w = new Vector(2,2);
assert((-v).x == -2 && (-v).y == -3); // -v == (-2,-3)
}
As of 8/21/2012 the Dart Specification Section 7.1.2 Operators, considers defining negate with a possible nullary type as bad style and should report to the user a static warning.
Defining a nullary method named negate or a binary method named equals
will have the same effect as dening an operator but is considered bad
style, and will cause a static warning.
[...]
It is a static warning if the return type of the user-declared
operator []= is explicitly declared and not void. It is a static
warning if the return type of the user-declared operator equals is
explicitly declared and is not bool. It is a static warning if the
return type of the user-declared operator negate is explicitly
declared and not a numerical type.
We have an open bug for this: http://code.google.com/p/dart/issues/detail?id=3416