Creating a new object of the same class as a given object - dart

So I have something like the following:
abstract class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }
I want to make a function makeABaby(Animal parent) which will call either the Cat() or Dog() constructor depending on which type was actually passed into the function, and return the new object.
I could do something like if(parent is Cat) { return Cat(); } etc. in the function, but that's hard to maintain as the number of animal species grows, so I'm looking for something a bit more dynamic. Ideally I'd like to do something like
Animal makeABaby(Animal parent) {
return parent.getConstructor().apply();
}
but I'm not sure how I can make that work in Dart, or if it's something that can be done without having to resort to reflection. Is there anything in the language that I can leverage for this?

You can use reflection in a package dart:mirrors.
import 'dart:mirrors';
abstract class Animal {
static Animal makeABaby(Animal parent) {
ClassMirror cm = reflectClass(parent.runtimeType);
InstanceMirror im = cm.newInstance(const Symbol(''), []);
var baby = im.reflectee;
return baby;
}
}
class Dog extends Animal {}
class Cat extends Animal {}
void main() {
var a = Dog();
var b = Cat();
print(Animal.makeABaby(a));
print(Animal.makeABaby(b));
}
Output:
Instance of 'Dog'
Instance of 'Cat'

Related

How Dart Upcasts automatically if I use 'is' keyword in the if statement

These are my classes,
class Animal{
Animal();
}
class Mammal extends Animal{
String name;
Mammal(this.name):super();
}
Inside my main method,
main() {
Animal dog = new Mammal("Tommy");
if(dog is Mammal)
print(dog.name); //prints "Tommy"
}
My dog object has the type Animal, how it can expose Mammal's property "name"? Dartpad is itself auto-suggesting me to use 'name' property.
Will, it upcast from Animal type to Mammal for me, because I'm certain that the dog object is a Mammal, once it enters that if statement. But how it works behind the scene?
The feature you're describing is called "type promotion", and it only occurs in places that the compiler can prove that is it valid.
For example:
function parameters:
void function(Animal animal) {
if (animal is Mammal) {
// animal now has a static type of Mammal
print(animal.name); // safe to access Mammal-only fields
}
}
local variables:
void function() {
Animal animal = Mammal('name');
if (animal is Mammal) {
// animal now has a static type of Mammal
}
}
However, it does not work for:
top level fields
Animal animal = Mammal('name');
void function() {
if (animal is Mammal) {
// animal still has static type Animal
}
}
or instance fields
class Foo {
Animal animal;
void function() {
if (animal is Animal) {
// animal still has a static type of Animal
}
}
}
This is because of Dart's getter syntax. In Dart, you can define getters using the following syntax:
Foo get foo {
return makeSomeFoo();
}
In fact, although most people think "getters are functions that look like fields", it is actually the opposite: fields automatically generate corresponding getters. This means that type promotion can't be used anywhere a field access could conceptually be a getter.
Since local variables and function parameters have no notion of "overriding getters", they are safe.
However, imagine the following top level getter:
Animal get animal {
if (Random().nextBool()) return Mammal('name');
else return Reptile('name');
}
Just because the first call to animal has returned a Mammal, doesn't mean subsequent calls always will, so it is not safe to assume that animal is now a Mammal.
Similar logic applies to classes, with the added complication that getters can override fields.
Note, you can easily get around the issue with instance members and top level getters by using a local variable:
Animal get animal => // some implementation
void function() {
final _animal = animal;
if (_animal is Animal) {
print(_animal.name); // totally safe
}
}
I've found the answer, it is called smart casting. Dart can do the casting automatically for local variables if it's inside a conditional statement that guarantees the type of the variable.

How to specify a data type for a container class in Dart

I am looking for the dart syntax to achieve the following but do not know what the correct terminology is to search for the answer.
class BaseClass <T extends MyType> {
List<T> items;
T getFirstItem() => items.first;
}
and then be able to Sub class like this
class ClassForMyType<MyType> extends BaseClass {
List<MyType> items;
MyType getFirstItem() => items.first;
}
Where ClassForMyType would extend BaseClass so that I do not have to re-implement the contrived getFirstItem() method.
Effectively I want to be able to use it like this:
ClassForMyType container = ClassForMyType();
MyType item = Mytype();
container.items.add(item);
List<MyType> itemsFromContainer = container.items;
MyType firstItem = container.getFirstItem();
I have tried something like this:
BaseClass<T extends MyType> {
List<T> items;
void addItemFromMap(Map map) {
items.add(T.fromMap(map));
}
}
The above fails on the .fromMap() which does exist on MyType. In other methods where I access other methods on MyType these appear to work, it seems to have a problem only wiht the named contructor.
I think the only way that would work and look relatively OK would be:
abstract class BaseClass<T extends MyType> {
List<T> items;
// abstract factory method
T itemFromMap(Map map);
void addItemFromMap(Map map) {
items.add(itemFromMap(map));
}
}
class ClassForMyType extends BaseClass<MyType> {
// implementing abstract factory method, delegating call to constructor
MyType itemFromMap(Map map) => MyType.fromMap(map);
}
I agree, a little bit overhead that you should implement factory method in every subclass, but that is the only proper way.

Question about of "Substituting types" from dart documentation

Reading the "Substituting types" section from "The Dart type system" I don't understand why I get the error from assigning from one level above from hierarchy.
class Animal { }
class Cat extends Animal { }
class MaineCoon extends Cat { }
Animal c = Cat(); works to the compiler, but MaineCoon c = Cat(); get incorrect constructor error.
I don't understand why MaineCoon couldn't be cast to Cat, but Animal can cast
Cat or even Animal c = MaineCoon(); still works.
Short answer (see source code below)
void main() {
Animal animal = Cat();
print(animal.animalName);
MaineCoon maineCoon = Cat();
// Opps!!! The "Cat" does not declares "maineCoonName".
print(maineCoon.maineCoonName);
}
class Animal {
String animalName = 'animal';
}
class Cat extends Animal {
String catName = 'cat';
}
class MaineCoon extends Cat {
String maineCoonName = 'maineCoonName';
}
How do you imagine accessing maineCoonName from instance Cat if Cat did not declare a member maineCoonName?
P.S.
Even if this member (I mean maineCoonName) is not declared, this does not mean that a similar member will not be added in the future, which, in principle, will break the entire program.

Can I redefine a superclass's property in a subclass? (c#'s new modifier maybe?)

Kennel has a Dog.
KennelWithPlayground (of type Kennel) has a DogWithABall (of type Dog) and cannot have just a normal Dog.
I want to be able to treat any type of Kennel like it has a normal Dog.
So, I would like to redefine a property of a super class, in a subclass. The new property will be a subclass of the property's superclass. Is this possible in Dart? (something like the 'new' modifier in c# maybe). Or is there another way to achieve this?
The following code does not work because in the KennelWithPlayground class, DogWithABall is not a valid override of Dog.
class Dog {
String get bark => "woof";
}
class DogWithBall extends Dog {
String get ballAction => "boing";
}
abstract class Kennel {
Dog dog;
}
class KennelWithPlayground implements Kennel {
DogWithBall dog; //**invalid override**
KennelWithPlayground(DogWithBall dog);
}
void processKennel(Kennel kennel){
kennel.dog.bark;
if (kennel is KennelWithPlayground)
print(kennel.dog.ballAction);
else
print("Bored dog");
}
There are two solutions that I can think of off the top of my head. You can use the covariant keyword to allow the override, at the expense of a runtime check, or you can use generics to track the types statically. For the former approach, you would change the definition of Kennel as follows:
abstract class Kennel {
covariant Dog dog;
}
Or alternatively, change the definition of KennelWithPlayGround:
class KennelWithPlayground implements Kennel {
covariant DogWithBall dog;
KennelWithPlayground(DogWithBall dog);
}
This will allow the override, but will cause a runtime check to happen on every write to the dog field to make sure that whatever is being written is actually a DogWithBall.
An alternative is to use generics as follows:
class Dog {
String get bark => "woof";
}
class DogWithBall extends Dog {
String get ballAction => "boing";
}
abstract class Kennel<DogKind extends Dog> {
DogKind dog;
}
class KennelWithPlayground implements Kennel<DogWithBall> {
DogWithBall dog;
KennelWithPlayground(DogWithBall dog);
}
void processKennel(Kennel kennel){
kennel.dog.bark;
if (kennel is KennelWithPlayground)
print(kennel.dog.ballAction);
else
print("Bored dog");
}
It is possible, but if and only if the property has no setter.
class Foo {
final num value;
Foo(this.value);
}
class Bar implements Foo {
final double value;
Bar(this.value);
}

Dart - Trying to understand the value of 'factory' constructor

If I am understanding correctly:
"A factory constructor affords an abstract class to be
instantiated by another class, despite being abstract."
As example:
abstract class Animal {
String makeNoise(String sound);
String chooseColor(String color);
factory Animal() => new Cat();
}
class Cat implements Animal {
String makeNoise(String noise) => noise;
String chooseColor(color) => color;
}
The above allows me to do this:
Cat cat = new Animal();
var catSound = cat.makeNoise('Meow');
var catColor = cat.chooseColor('black');
print(catSound); // Meow
And it also prevents me from doing this:
class Dog implements Animal {
int age;
String makeNoise(String noise) => noise;
String chooseColor(color) => color;
}
Dog dog = new Animal(); // <-- Not allowed because of the factory constructor
So if I am correct with all this, I am led to ask why the extra code for Animal?
If you intend on using a factory constructor for animal, which creates only cats, why not just have a Cat class with the required methods/properties?
Or, is the purpose of the Animal class with a factory constructor like above, really an interface specifically designed for Cat class only?
I don't think that the problem in the factory.
Your code initially was wrong.
Look at this code and make your conclusions.
Locomotive locomotive = new SomethingWithSmokeFromChimney();
Now look at this code.
Plant plant = new SomethingWithSmokeFromChimney();
You mistakenly believe that all animals on the earth (even the dogs) are cats.
Cat cat = new Animal();
If you want this.
Cat cat = new Animal();
Dog dog = new Animal();
Then (if I correctly understand you) you also want this.
// Cat cat = new Animal();
// Dog dog = new Animal();
Dog dog = new Cat();
P.S.
The same wrong conclusions but without factory.
void main() {
Cat cat = new Animal();
Dog dog = new Animal();
}
class Animal {
}
class Cat implements Animal {
}
class Dog implements Animal {
}
But this code (depending on documenation) may be considered correct.
void main() {
Cat cat = new Animal("cat");
Dog dog = new Animal("dog");
}
abstract class Animal {
factory Animal(String type) {
switch(type) {
case "cat":
return new Cat();
case "dog":
return new Dog();
default:
throw "The '$type' is not an animal";
}
}
}
class Cat implements Animal {
}
class Dog implements Animal {
}
The factory constructor of the abstract class can return (by default) some default implementation of this abstract class.
abstract class Future<T> {
factory Future(computation()) {
_Future result = new _Future<T>();
Timer.run(() {
try {
result._complete(computation());
} catch (e, s) {
result._completeError(e, s);
}
});
return result;
}
}
I would see Cat as the default implementation of Animal.
Of course you can't do Dog dog = new Animal(); because new Animal(); returns a Cat but you can do Dog dog = new Dog(); or Animal animal = new Dog();
EDIT to long for a comment
:) This is just one other example how you can utilize factory constructor. Try to see the factory constructor as a normal function (top level function or static class function) that returns an object. To make the user of the class unaware of what is going on behind the scene allow him to use it like a constructor with new SomeClass. That is what a factory constructor is. The first use cases that comes to mind are usually an implementation for the singleton pattern or for caching purposes but also all other cases where it looks for the user of the class as if he just creates a new instance but in fact he gets something constructed and prepared in a way that is more complicated but he needs not to be aware of.
The one characteristic of a factory constructor is that the object is not already created at the method start. In the method you can have various mechanisms to create the object, like for example:
abstract class Animal {
String makeNoise(String sound);
String chooseColor(String color);
factory Animal() {
var random = new math.Random();
if (random.nextBool() == true)
return new Cat();
else
return new Dog();
}
}

Resources