2

I am trying to up-cast the subclass object but it is not working. The following program compiles without any errors.

VideoStreamModel model = VideoStreamModel("");
VideoStream entity = model;
print(model); // prints VideoStreamModel
print(entity); // prints VideoStreamModel
print(entity as VideoStream); // prints VideoStreamModel
print(cast<VideoStream>(model)); // prints VideoStreamModel

I have written a testcase to test the relation of above two classes and it passes.

test('should be a subtype of VideoStream', () async {
    expect(model, isA<VideoStream>());
});

What could be the problem here?

EDIT:

[deleted]

EDIT 2:

[deleted]

Edit 3:

Here is the complete code reproducing the error.

import 'package:equatable/equatable.dart';
import 'package:test/test.dart';

class A extends Equatable {
  final String x;
  
  A(this.x);

  @override
  List<Object> get props => [x];
}

class B extends A {
  B(String x) : super(x);

  A method() {
    B b = B(x); // doing A b = A(x) makes the test pass
    return b;
  }
  
}

void main() {

  B b = B("");

  test('test', () async {
      final expected = A(b.x);
      final actual = b.method();
      expect(actual, expected);
  });
}

It generates the following assertion error:

Expected: A:<A>
  Actual: B:<B>
Nakash Kumar
  • 60
  • 1
  • 7

1 Answers1

0

print is calling the toString() on the object you are pointing at (in this case VideoStreamModel) which knows what type it is. When you are casting, you are not changing anything about the object itself but only how the compiler should see the object when it determines if you are allowed to use a given typed variable to point to the object.

So when you are doing entity as VideoStream you are really just telling the compiler that you "promise" that the entity can be seen as a VideoStream. But on runtime, this cast will be tested to see if it is true.

All of this is really not an issue since you should never test for the specific type of the object when you are programming Dart but instead use the is operator which tests if a given object is compatible with a given interface.

So e,g, (entity is VideoStream) will return true.

Updated part

You problem seems to be a misunderstanding of the use of Equatable. It is important to notice that Equatable are not only using the elements from props to determine if two objects are equal but it also looks at the runtimeType. You can see this from the implementation:

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Equatable &&
          runtimeType == other.runtimeType &&
          equals(props, other.props);

https://github.com/felangel/equatable/blob/master/lib/src/equatable.dart#L46

This means that:

  A a = A("");
  B b = B("");
  print(a == b); // false

When you are using expect without any matcher, it will just make an == operation which is stated in the documentation:

matcher can be a value in which case it will be wrapped in an equals matcher

Since we (as stated before) cannot change the runtimeType of an object after its creation you need to implement your own == if you want the two object instances to be seen as equal since equatable does only see two objects as equal if they both is created from the same class and contains the same values defined with props.

julemand101
  • 28,470
  • 5
  • 52
  • 48