I am learning Dart and am facing an issue with understanding the way inheritance works here.
I have 3 classes Alphabet, A which extends Alphabet and B which extends Alphabet and implements A. Something like this:
class Alphabet {
void define() {
print("this is an alphabet");
}
}
class A extends Alphabet {
void func1() {
print("called func1");
}
void func2() {
print("called func2");
}
}
class B extends Alphabet implements A {
#override
void func1() {
print("called B:func1");
}
#override
void func2() {
print("called B:func2");
}
}
Now, the problem is this works, even though I haven't defined the define() function from A, the function that A inherits from Alphabet.
My questions are :
In class B, which define() function do I get, A.define() or Alphabet.define()?
And if the define() function that I get is from Alphabet, then doesn't it defeat the purpose of implementing a class? As class A has 2 of its own functions and one that it inherits from Alphabet. So, it should tell me to override 3 functions.
NOTE: When I remove extends Alphabet from the definition class B, it shows the expected behavior, saying that I need to implement the define() function. That makes sense.
When you write B extends Alphabet, that means that B provides the interface of Alphabet and inherits the implementation of Alphabet.
When you write B implements A, that means that B provides the interface of A but does not use any of A's implementation.
B extends Alphabet implements A does what you requested: it provides the interfaces of both Alphabet and A (and the interface of A is a superset of Alphabet's), and it uses the implementation from Alphabet but not from A. B.define() therefore will call the Alphabet.define implementation.
And if the define() function that I get is from Alphabet, then doesn't it defeat the purpose of implementing a class? As class A has 2 of its own functions and one that it inherits from Alphabet. So, it should tell me to override 3 functions.
No. If it worked that way, then it would defeat the purpose of extends A. If you want to force B to override all of the functions provided by A's interface, then simply write B implements A.
Related
Minimal reproducible code:
enum Foo {
a,
b;
String get name {
switch (this) {
case Foo.a: return 'A';
case Foo.b: return 'B';
}
}
}
void main() {
printEnum<Foo>(Foo.values);
}
void printEnum<T extends Enum>(List<T> list) {
for (var e in list) {
print(e.name);
}
}
The for loop prints
a
b
But I wanted it to print
A
B
So, how do I override the name property in the enum?
Note:
Using (e as Foo).name will solve the issue, but I have many enums in my project, so I can't cast them like this.
Also, please don't post answers like, use toUpperCase(), etc, because I just provided a simple example, but in real world, things are quite different.
You cannot override the name getter "on Enum" because it's an extension getter, not an instance getter.
Overriding, aka. late binding, of instance members only apply to actual (virtual) instance members.
Extension members are statically resolved, so a call of .name either hits the extension member, or it doesn't, and it depends entirely on the static type of the receiver. If you have an Enum e; ... e.name ... then it will call the extension member, and there is absolutely no way to change that, or change what it does.
If you want to have a custom and overridable name getter, I'd introduce an interface like
abstract class NamedEnum extends Enum {
String get name;
}
and then let all your enums implement NamedEnum.
Then you can do (enumValue as NamedEnum).name for any of your enums.
It won't interact with other enum types' extension name getter.
Casting e as dynamic works, as long as you ensure that the name property exists on the enum you are printing. Ex:
void printEnum<T extends Enum>(List<T> list) {
for (var e in list) {
print((e as dynamic).name);
}
}
Is it possible to call a second ancestor method in dart? something like super.super.hello()? If it's possible, how can it be written?
class A {
void hello() {
print('A');
}
}
class B extends A {
#override
void hello() {
print('B');
}
}
class C extends B {
#override
void hello() {
// How to call the hello() implemented in the class A here?
}
}
void main() {
var c = C();
c.hello();
}
It's not possible.
The reason it's not possible is that it breaks abstraction.
When you look at class C extend B, all you need to know about B is which signatures its members has and which interfaces it implements. As long as that stays effectively the same, your valid code will keep working.
Consider what would happen if the author of B decided to make themselves a helper base-class:
abstract class _BaseB extends A {
String get _myName;
#override
void hello() {
print(_myName);
}
}
class B extends _BaseB {
#override
String get _myName => "B";
}
That's a perfectly valid refactoring. The resulting class B has all the same members and implements all the same interfaces (and also _BaseB, but it's private so nobody can see that).
The C class above would keep working if all it does is to call super.hello(). If it had a way to ask for super.super.hello(), that might no longer be valid.
Similarly if the B class was changed to:
class B implements A {
#override
void hello() {
print("B");
}
}
(changing extends to implements), then all methods of B works the same as before and it implements the same interfaces. Again, there is no visible differences to the users of the B class.
But if you could call something like A.super.hello() to reach the A class's hello method, then that would now break because that method isn't in the B class at all.
So, by restricting super.hello() to only call methods on the precise class you write as the superclass, you are prevented from introducing dependencies on the implementation of B, dependencies which would make otherwise valid refactorings into breaking changes.
So I was just playing with the protocols and just got confused with the following behaviour.
I have a protocol with the function and a base class with the same function name. Now, another class is inheriting from a base class and implementing a protocol. But it is not producing error in some scenarios.
For example:
protocol P {
func greetings()
}
class Base {
func greetings() {
print("Hello Base")
}
}
class A: Base {
override func greetings() {
print("Hello Class")
}
}
extension A: P {
}
let a = A()
print(a.greetings()) // print: Hello Class
let b:Base = A()
print(b.greetings()) // print: Hello Class
let p:P = A()
print(p.greetings()) // print: Hello Class
So, how is the conformance of the class to the protocol happening?
Also in the following case, when protocol function has default implementation, it compiles without any error and always opt from base class function.
protocol P {
func greetings()
}
extension P {
func greetings() {
print("Hello Protocol")
}
}
class Base {
func greetings() {
print("Hello Base")
}
}
class A: Base {
}
extension A: P {
}
let a = A()
print(a.greetings()) // print: Hello Base
let b:Base = A()
print(b.greetings()) // print: Hello Base
let p:P = A()
print(p.greetings()) // print: Hello Base
Why it always opt for a base class function? And Why no compile time error because of ambiguous function call?
The type of the variable you assign an object to does not control how functions are dispatched on that object. When looking for a function, the computer starts at the object and walks "up" through the class hierarchy looking for an implementation. ie. It starts at A and if it doesn't find a function, it goes up to Base (A's superclass) and looks there. If still not found it goes up to the next superclass (which there isn't one in this case). If the function isn't found then you get an error.
In your first example you have an instance of A and A overrides the base greetings so that is that implementation that will always be used. This is related to an important concept in Object Oriented Programming - Substitutability.
In this case you can declare a variable of type Base but assign an instance of a more specialised class - A, but the program still operates correctly.
Remember that type inference as shown in your first assignment (let a = A()) is not present in all languages. In many cases you need to explicitly declare a variable's type. If declaring a variable of type Base meant that the Base function was called instead of subclass, it would defeat the purpose of subclassing.
With regard to your question
how is the conformance of the class to the protocol happening?
A protocol is a specification but not an implementation (we will look at your question around default implementations next). Your protocol says that all things that conform to P must provide a greeting function. Clearly both Base and A provide a greeting function. Your extension merely adds the stipulation that A conforms to P. It doesn't need to add the implementation of P because the greeting method already exists.
If you removed the extension A: P then let p:P = A() would give an error because the compiler no longer knows that A conforms to P.
This brings us to your last two questions:
Why it always opt for a base class function?
And Why no compile time error because of ambiguous function call?
From The Swift Programming Language
You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.
In your second example block, A doesn't override the Base implementation of greeting, so the Base implementation is always called.
The default implementation of greeting that you added to P will only be used in the case where a class that conforms to P doesn't provide an implementation.
Default implementations can make it easier to conform to a protocol by supplying implementation where there is some behaviour that will apply to all or a majority of cases. Default implementations are always ignored if the conforming class provides an implementation
Consider the following example:
Protocol Vehicle {
func soundHorn()
var numberOfDoors: Int
}
extension Vehicle {
func soundHorn() {
print("Beep")
}
}
class SportsCar: Vehicle {
var numberOfDoors: Int {
return 2
}
}
class Sedan: Vehicle {
var numberOfDoors: Int {
return 4
}
}
class Truck: Vehicle {
var numberOfDoors: Int {
return 2
}
func soundHorn() {
print("**HONK**")
}
}
Here we have defined a simple protocol, Vehicle, that says that a Vehicle has a number of doors and can sound its horn. We provided a default soundHorn that prints "Beep". We then declared classes that implement this protocol. The two different types of car have different numbers of doors but we are happy with the default horn. For a Truck we have a bigger, louder horn, so we implement soundHorn rather than using the default:
var v: Vehicle = SportsCar()
v.soundHorn() // Prints "beep"
v = Truck()
v.soundHorn() // Prints "**HONK**"
Note how v is of type Vehicle but we get the correct horn based on the object that is actually assigned to v. The compiler is happy because it knows that all Vehicles can soundHorn; it doesn't need to know the specific vehicle type.
I have the following code:
class A {
void m() {
print("hello");
}
}
mixin B {
void m() {
print("mixin class b");
}
}
class C extends A with B {
void m() {
print("m of c");
super.m();
}
}
void main() {
C cc = C();
cc.m();
}
Here when I write super.m() it is referring to the mixin class rather than m() of class A, why? Which exactly is its superclass referenced by super.
How can I then call m() of class A from C?
I don't think it is possible to get the m method on A in your example. The reason is by using mixins you are telling the compiler you want to extend A but please insert all methods from B even if one of the methods overrides an existing one. So after compile, the compiled class of C ends up not even containing A.m.
There are some details about this behavior in this older article and with some explanation about why this is the case: https://medium.com/flutter-community/https-medium-com-shubhamhackzz-dart-for-flutter-mixins-in-dart-f8bb10a3d341
I'm trying to bind a class C from a third-party's package.
It injects a class Foo instance via constructor -
class C {
public C(#Inject Foo foo) {
...
}
...
}
In my application, I've two instances of Foo bound -
bind(Foo.class)
.to(FooImpl1.class);
bind(Foo.class)
.annotatedWith(Names.named("SpecialFoo"))
.to(FooImpl2.class);
when C is bound, I want the Named Foo instance to be used. However I do not have access to the code in which C is defined, to be able to put any annotations.
Is there a suggested way of doing that, short of writing my own provider method for C?
You could look into using PrivateModule. In your example, it will be something like:
public class CModule extends PrivateModule {
protected void configure() {
bind(Foo.class).to(FooImpl2.class);
bind(C.class);
expose(C.class);
}
}