Ruby Type System
Ruby is a dynamically typed and interpreted language. That means that the program is not compiled before the execution to any type of bytecode, and there is no type checking before execution. This may provide to some problems which does not exists in static typed programming languages, but also provides the real freedom for the developer.
Because the variable type is not defined statically and it can change while the program is running, Ruby provides a bunch of methods to determine the class of the instance. Every object has build-in
class method, which returns the constant of class name (not a string!). You can check if the object is instance of the given class by running
instance_of?(class) method. There is also a function
is_a?(class) on every instance, returns true if the
class is the class or any of superclass of the given object. The difference between
is_a? is that
is_a? is checking all superclasses of the variable.
Lets see this on example
Server class from previous chapter:
win = WindowsServer.new('kirk', '.starfleet.com') # WindowsServer is a subclass of Server #=>* WindowsServer: kirk.starfleet.com * unix = UnixServer.new('yoda', '.rebelians.org') # UnixServer is a subclass of Server as well #=>* UnixServer: yoda.rebelians.org * win.class # class of the win variable is WindowsServer #=>WindowsServer win.is_a? WindowsServer # win is a WindowsServer #=>true win.is_a? Server # win is a Server as well, because Server is a superclass of WindowsServer #=>true win.is_a? UnixServer # but is is not an UnixServer #=>false win.instance_of? WindowsServer # win is an instance of WindowsServer class #=>true win.instance_of? Server # but not the instance of Server (instance_of? is not checking superclasses) #=>false
is_a? method may be replaced by triple equal sign
=== operator, called case equality operator. The left operand is a class, the right one is an instance. It works like
is_a? - checking superclasses as well.
WindowsServer === win #=>true Server === win #=>true
Unlike the other languages there is no explicit type conversion system in Ruby. With your own class you may provide methods which converts your object to other classes, if it makes sense. You may call this methods whatever you want, but it is a good practice to use Ruby convention. Thus, to convert object to integer, use
to_s - to string,
to_a - to array, etc.
Let's see it in example. Consider you want to create an object to keep the IP numbers (you can store it in string or in number, but here is a bit more sophisticated version).
class Ip def initialize(p1, p2, p3, p4) @p1 = p1; @p2 = p2; @p3 = p3; @p4 = p4 end end ip = Ip.new(192,168,1,10) #=>#<Ip:0x007faf2b0f78a0 @p1=192, @p2=168, @p3=1, @p4=10>
It is a quite natural to convert the ip to string, giving it human readable, well-known format:
class Ip def to_s @p1.to_s + "." + @p2.to_s + "." + @p3.to_s + "." + @p4.to_s end end ip.to_s #=>"192.168.1.10"
Or even convert it to the 32-bit number:
class Ip def to_i 256*256*256*@p1 + 256*256*@p2 + 256*@p3 + @p4 end end ip.to_i #=>3232235786
Or represent it as binary or hexadecimal. Notice that
to_s method on number object may take an argument - the base, so to convert to binary, use
to_s(2), to convert to hex -
to_s(16). It is working in the other way as well - to convert "ff" string to integer, add a base to
class Ip def to_bin to_i.to_s(2) end def to_hex to_i.to_s(16) end end ip.to_bin #=>"11000000101010000000000100001010" ip.to_hex #=>"c0a8010a"