Consider the following class:
class MyClass<T extends num> {
const MyClass();
void instanceFunction() {}
static void staticFunction<T extends num>() {}
}
The following expressions are all true
:
identical(const MyClass(), const MyClass<num>());
identical(const MyClass.new(), const MyClass<num>.new());
identical(const MyClass().instanceFunction, const MyClass<num>().instanceFunction);
identical(MyClass.new, MyClass.new);
identical(MyClass<num>.new, MyClass<num>.new);
identical(MyClass.staticFunction, MyClass.staticFunction);
identical(MyClass.staticFunction<num>, MyClass.staticFunction<num>);
But when the base type, num
, is included in one tear-off, such as in the expressions below, they are false
:
identical(MyClass.new, MyClass<num>.new);
identical(MyClass.staticFunction, MyClass.staticFunction<num>);
Why is this the case?
CodePudding user response:
The true
cases:
identical(const MyClass(), const MyClass<num>());
You instantiate an MyClass
object. Since MyClass
is declared with MyClass<T extends num>
, MyClass()
with no explicit type specified is shorthand for MyClass<num>()
. This expression therefore is the same as identical(const MyClass<num>, const MyClass<num>)
, which should obviously be true.
identical(const MyClass.new(), const MyClass<num>.new());
This is just a more roundabout version of the previous case.
identical(const MyClass().instanceFunction, const MyClass<num>().instanceFunction);
This is true for the same reason as the first case. const MyClass()
is shorthand for const MyClass<num>()
, const
instances are canonicalized, so you're comparing .instanceFunction
members of the exact same object.
identical(MyClass.new, MyClass.new);
Both arguments are the same and both are statically-known at compile-time, so there's no reason for this to be false.
identical(MyClass<num>.new, MyClass<num>.new);
This would be true for the same reason as the previous case.
identical(MyClass.staticFunction, MyClass.staticFunction);` identical(MyClass.staticFunction<num>, MyClass.staticFunction<num>);
Both arguments to identical
are the same, so that these evaluate to true
shouldn't be surprising.
The false
cases:
identical(MyClass.new, MyClass<num>.new); identical(MyClass.staticFunction, MyClass.staticFunction<num>);
MyClass.new
and MyClass.staticFunction
are not shorthand for MyClass<num>.new
and MyClass.staticFunction<num>
respectively; they evaluate to generic functions:
var c1 = MyClass.new;
var c2 = MyClass<num>.new;
var f1 = MyClass.staticFunction;
var f2 = MyClass.staticFunction<num>;
then the type of c1
is MyClass<T> Function<T extends num>()
and the type of c2
is MyClass<num> Function()
. Similarly, the type of f1
is void Function<T extends num>()
but the type of f2
is void Function()
. c1
and f1
are still generic; you can do c1<num>()
or f2<double>()
later.
c2
and f2
have the type parameter statically bound already, so they have a different type and clearly should not be the same as c1
and f1
respectively.
A more interesting case is:
identical(f1<num>, MyClass.staticFunction<num>))
which evaluates to false
. Why isn't this the same as the identical(MyClass.staticFunction<num>, MyClass.staticFunction<num>)
case? I'm not sure exactly why, but I would presume that it's an implementation detail from the Dart compiler being able to trivially canonicalize MyClass.staticFunction<num>
at compilation-time but not f1<num>
due to f1
being potentially variable.