# (sort of) casting types in Ruby: changing class of an instance [closed]

I defined the class Rectangle:

class Rectangle
def initialize(b, h)
@b = b
@h = h
end
def area
@b*@h
end
def to_s
"Rectangle #{@b}x{@h}"
end
end


and its subclass Square:

class Square < Rectangle

def initialize(s)
@s = s
super(@s, @s)
end
def to_s
"Square of side #{@s}"
end
end


Now, let's say I defined a Rectangle object r = Rectangle.new(5, 5), but actually it should be a Square because I'd like r.to_s to return "Square of side 5".

I can define a to_square method in the Rectangle class that returns a Square object equivalent to r, but, is it possible to write a to_square! method that would actually change the r class to Square without returning another object, so that r.class would now return Square instead of Rectangle?

And, what if I'd like:

Square(r)


to return the Square equivalent object of r, just like:

Integer("3")


which returns the Integer 3?

Is that possible? And, if so, how?

• Don't close questions if you can migrate them to SO! – user13328 May 22 '14 at 0:19
• the hall monitors strike again. i wish you guys wouldn't do this. i got here from google, it was exactly what i needed and rather then find help, i find know it alls telling the submitter and me how stupid we are. – user1130176 Oct 11 '18 at 16:08

First off, why bother with a separate Square class?

Personally, I'd make a Rectangle class' to_square! method simply set both width and height to Math.sqrt(area). The result is still a Rectangle, just a square one.

The Rectangle's initializer method could also be written with an optional second argument; If there's only one argument, use that for both width and height, and you have a square.

Furthermore, I'd add a square? method. Checking obj.square? is, in my view, preferable to checking obj.class == Square. You could also use square? in to_s to return one string or the other.

To answer your question, though, I don't know of any way to just change an instance's class. You could however experiment with including different modules depending on whether the rectangle in question is a square or not. But that all seems pretty complex.

Finally, if you want a Square method, simply define one:

def Square(side)
Rectangle.new(side, side)
end


Even if you have a Square class already, you can still have a method of the same name:

class Square
# ...
end

def Square(side) # no naming conflict
Square.new(side)
end

• Because I'm studying inheritance and subclassing :-) – DuArme Oct 7 '12 at 13:07
• @DuccioArmenise Ah, well that explains it :) In that case, I'd definitely go with tokland's suggestion of a factory method on the class, or a Square() factory outside the class (like you see above), and/or have Rectangle#to_square (no bang) return a new Square instance. Don't start changing the class of an instantiated object, even if you can (and again, I doubt you can - it'd get confusing in a hurry). – Flambino Oct 7 '12 at 13:27
• Ok, but what about if r, instance of Rectangle, can't be converted in a Square because it isn't a square. Conventionally speaking, it would be right to assume that r.to_square should return false while r.to_square! should raise an exception? – DuArme Oct 7 '12 at 13:40
• @DuccioArmenise Depends on what you want to enforce. I imagine a to_square that returns a square with the same area as r, regardless of whether r is already a square. Same as calling to_i on a float returns an int even if the float can't be perfectly expressed as an int; to_i doesn't raise. Alternatively, you could have a to_square that raises an exception for a non-square r, while a to_square! forcibly returns a "squared square". However, in this context, I'd say that's a bad use of the bang-suffix. – Flambino Oct 7 '12 at 14:14
• Just found this statement of Matz's about it: "The bang (!) does not mean "destructive" nor lack of it mean non destructive either. The bang sign means "the bang version is more dangerous than its non bang counterpart; handle with care" (wobblini.net/bang.txt) And, from the Pickaxe: "Methods that are dangerous, or that modify their receiver, may be named with a trailing exclamation mark" (ruby-doc.org/docs/ProgrammingRuby/html/tut_methods.html). :-) – DuArme Oct 7 '12 at 14:31

If you make the expression ClassName.new return something different than an instance of ClassName you will be breaking the programmer's expectations. The most idiomatic, and simple, way is writing a factory method:

class Rectangle
# same code you have

def self.with_sides(b, h)
b == h ? Square.new(b) : Rectangle.new(b, h)
end
end

Rectangle.with_sides(1, 2).to_s # Rectangle 1x2
Rectangle.with_sides(2, 2).to_s # Square of side 2


Of course, you can always do black magic with Ruby's new, but I'd advise against it.

• Really interesting answer and article, thank you. But I didn't ask for ClassName.new to return something different from an instance of ClassName. Instead I asked if it's possible to change the class of an existing instance in Ruby (I guess it's not possible, as in Java, and for good reasons, but I don't know for sure yet). And I agree with @Flambino when he sais that even if it would be possible it would be a bad idea. – DuArme Oct 7 '12 at 13:35