xGhost
xGhost

Reputation: 126

Perl Moose parent class cast with child

package Point;
use Moose;

has 'x' => (isa => 'Int', is => 'rw');
has 'y' => (isa => 'Int', is => 'rw');

package Point3D;
use Moose;

extends 'Point';

has 'z' => (isa => 'Int', is => 'rw');

package main;

use Data::Dumper;

my $point1 = Point->new(x => 5, y => 7);
my $point3d = Point3D->new(z => -5);

$point3d = $point1;
print Dumper($point3d);

Is it possible to cast a parent to child class such as c++? In my examble is $point3d now a Point and not a Point3D include the Point.

Upvotes: 4

Views: 1289

Answers (4)

phaylon
phaylon

Reputation: 1923

Take a look at the Class::MOP documentation on CPAN, especially the clone_object and rebless_instance methods:

sub to_3d {
  my ($self, %args) = @_;
  return Point3D->meta->rebless_instance(
    $self->meta->clone_object($self),
    %args,
  );
}

And then use it like the following:

my $point_3d = $point->to_3d(z => 7);

This will also take care to treat the newly specified %args as if they've been passed in by the constructor. E.g. builders, defaults, and type constraints are all considered during this build.

Upvotes: 5

DVK
DVK

Reputation: 129413

Basically, I am seconding what cdhowie and Dave S. said, but one more thing to add.

If you DO want to make $point3d which holds an object of class Point into a real object of subclass Point3D, the correct OO way of doing it is by creating a constructor new_from_Point() in Point3D class, which takes an object of class Point as input and creates an Point3D object (it should probably take an extra "z" parameter). The C++ equivalent would be a constructor with a signature of (const Point &, double &z=0.0)

Upvotes: 0

Dave Sherohman
Dave Sherohman

Reputation: 46187

Well, Dumper should tell you that $point3d is now a Point, not a Point3D, because your assignment of $point3d = $point1 makes $point3d a second reference to the same object as $point1. The Point3D instance that was originally referenced by $point3d is now lost in space with a refcount of 0, making it eligible for garbage collection.

As cdhowie said, you don't really do typecasting in Perl the way you do in C/C++. The closest I can think of is to fall back on the non-OO calling convention and use, e.g., Point3D::z($point1, 4) to give $point1 a z-index of 4, but that's kind of clumsy and you'd have to use the same syntax for any future references to it's z-index. Note also that, when using this calling convention, Point3D must actually define a z method[1] or you'll get a runtime error because inheritance doesn't work when you do it this way because you're referring to Point3D as a package, not to $point1 as an object.

If you want to actually make your Point into a Point3D, you can easily change the actual type of an object using bless (the same command used to change a plain reference into an object in the first place, although that's hidden inside of Moose in your example code), but I suspect that manually reblessing a Moose object would anger the Moose. (But, if that is the case, I'm sure Moose provides a safer way of changing an object's class. I just don't use Moose enough to know what it would be.)

[1] ...or AUTOLOAD, but that's an entirely different can of worms.

Upvotes: 1

cdhowie
cdhowie

Reputation: 169028

You do not need to cast in Perl, since it is a dynamic language. In your case though, the variable $point3d contains a reference to a Point object at the end of the script. You cannot treat this as a Point3D because it is not a Point3D. You could manually convert it, but you can't "remake" an existing object as a different class. (Well, you theoretically can in Perl, but you shouldn't.)

Upvotes: 2

Related Questions