Today, let's have a look at classes in Ruby. You can get basic information about these animals under the links on the right, here I'm going to clarify some more unclear areas, like:
- Constructors,
- Static methods,
- Methods overloading,
- Fields.
Constructors
How to make a new object of a class? Well, in most old-school languages there are constructors. They are some sort of a hybrid methods: they are called as if they were static methods, but inside them, it was just like inside a normal methods - you can access object fields and call other its methods. In Ruby the two parts - static and nonstatic - are separated. In fact, you make a new object of class Klass, like this:
Once the object is constructed, the magical method wants to initialise the object, so that it can get some specific attributes (until now, this object's only attribute is its class). To do this, it calls object's method
A small example to test what I just said:
Static methods
Prepare for a shock now: there are no static methods in Ruby. Yes, you read correctly. What I was calling a static method (and I'm going to use this naming convention normally because it makes things easier) is in fact an instance method of the class object. Because you must know that all classes, like
So, for instance, Array is an object of the class Class. So when we write
But OK, if it's getting too hard for you, just remember that what seems to be static methods, is not quite the same as static methods in other languages. But it is similar.
Just a code sample
One day I'll explain why methods can be declared in so many ways. For now just remember that they are all alike.
Methods overloading
Today's a negative day: there's no method overloading in Ruby. If you define a method, and then define another method with the same name, the last one is the one that will really be accessible. There's no way to call the first one. Try it yourself, change a method in one of the standard classes:
As you see, the newly created method
So how can you create a method that can receive either String or Array or possibly Integer? Well, Ruby is a dynamically typed language so of course if you just define the method, it can receive arguments of any type. But if you want to serve them differently, then you must use a condition and check the class of the passed object:
So that's how you can emulate overloading. But the truth is that we can simplify a lot the last example:
Of course the behaviour is different (e.g. you'll get
Another trick to emulate method overloading is default parameters, like here:
As you see, the default values can be variable, and even very variable as
One more trick about overloading: let's say we want to receive a list (Array) of Strings into our method, and print them, like here:
But this has a little inconvenience - if you happen to pass just one String to the method, you must write
The method
Last one example, without a deep explanation for now:
Fields
It is not elegant to declare public fields in classes. In Ruby it is also not possible. Fields in classes look like this:
Of course you can write some more interesting code, like value validation and so on, especially in the setter. But if this trivial setter/getter is what you need, you can use an abbreviation:
Don't look at these colons in this way, they won't bite. We'll get to Symbols later, for now just remember how to define the attribute accessors in trivial and in nontrivial way.
One last remark about fields: they are always defined. If you run a new irb console and write
you'll just see
All for today, thanks for listening.
- Constructors,
- Static methods,
- Methods overloading,
- Fields.
Constructors
How to make a new object of a class? Well, in most old-school languages there are constructors. They are some sort of a hybrid methods: they are called as if they were static methods, but inside them, it was just like inside a normal methods - you can access object fields and call other its methods. In Ruby the two parts - static and nonstatic - are separated. In fact, you make a new object of class Klass, like this:
Klass::newor this:
Klass.new. It doesn't resemble classic constructor calling which would be more like
new Klass. In fact, what you do in Ruby, you call a static method
new
. This method is the method of each class as it is the method of the classClass
. And this Class::newdoes some magic: it creates a new object, allocates memory for it, makes it be this or that class, and in general performs some action that is not possible anywhere else in Ruby, only in
Class::new.
Once the object is constructed, the magical method wants to initialise the object, so that it can get some specific attributes (until now, this object's only attribute is its class). To do this, it calls object's method
initialize
, which is a perfectly regular instance method - not static, not even a bit. So that explains why you declare the method initialize
and then call new
to create the object. The arguments you pass to new
are completely ignored inside Class::new, they are only sort of forwarded to object's initialiser. (Later you'll learn how to do such a forwarding.)
A small example to test what I just said:
class K
def initialize(a,b,c)
puts "a=%d and b=%s and c=%02X"%[a,b,c]
end
end
K::new(131,"quale",63)
Static methods
Prepare for a shock now: there are no static methods in Ruby. Yes, you read correctly. What I was calling a static method (and I'm going to use this naming convention normally because it makes things easier) is in fact an instance method of the class object. Because you must know that all classes, like
String
or Array
or anything, are in fact objects (instances) of the class Class
. You can check it:5.class #=> Fixnum
"abc".class #=> String
Array.class #=> Class
Integer.induced_from(8.3), it might look like a static method of the class
Integer
, but it is not quite it. That's why you cannot say 10.induced_from(8.3), even though
10.is_a? Integer, so you could call a static method in this way in languages like C++ or Java. (
10.is_a? Integerthis is how you check if an object is an instance of the given class;
10.class==Integerwouldn't work because
10.classis in fact
Fixnum
and Fixnum
is an ancestor of Integer
).But OK, if it's getting too hard for you, just remember that what seems to be static methods, is not quite the same as static methods in other languages. But it is similar.
Just a code sample
class K
def self.static_method1
puts "in static 1"
end
class << self
def static_method2
puts "in static 2"
end
end
end
def K.static_method3
puts "in static 3"
end
K.static_method1
K.static_method2
K.static_method3
Methods overloading
Today's a negative day: there's no method overloading in Ruby. If you define a method, and then define another method with the same name, the last one is the one that will really be accessible. There's no way to call the first one. Try it yourself, change a method in one of the standard classes:
class String
def length(q)
puts q
end
end
"OneTwoThree".length(456)
length
was called. There is no way to call the old one: "aString".lengthyields an error now.
So how can you create a method that can receive either String or Array or possibly Integer? Well, Ruby is a dynamically typed language so of course if you just define the method, it can receive arguments of any type. But if you want to serve them differently, then you must use a condition and check the class of the passed object:
def test_class(arg)
if arg.is_a? String
puts "Called with String."
elsif arg.is_a? Array
puts "Called with Array."
elsif arg.is_a? Integer
puts "Called with Integer."
else
puts "Called with something else."
end
end
def test_class(arg)
puts "Called with %s."%arg.class
end
Fixnum
instead of Integer for numbers), but that's more ruby'ish, and the function looks nicer, too.Another trick to emulate method overloading is default parameters, like here:
def def_par(a,b=6,c=a+b)
puts c
end
def_par(3) #=> 9
def_par(3,9) #=> 12
def_par(3,9,2) #=> 2
c
here.One more trick about overloading: let's say we want to receive a list (Array) of Strings into our method, and print them, like here:
def str(list)
list.each{|s| puts s}
end
str(["string"]). So we'd like to be able to pass one String, or Array of Strings. We could of course check the class of the argument but let's move on and learn something else:
def str(list)
Array(list).each{|s| puts s}
end
Array
(it's a method here, not a class) returns an array containing one element - the argument, or returns just the argument if the argument is already an Array. So that's exactly what we needed.Last one example, without a deep explanation for now:
def str(*list)
list.each{|s| puts s}
end
str("one")
str("one","two","three")
str(*["one","two","three"])
Fields
It is not elegant to declare public fields in classes. In Ruby it is also not possible. Fields in classes look like this:
@field
. They can be accessed only from within the object, they cannot even be accessed from inside other object of the same class (what distinguishes Ruby from C++ or Java). So how to make them visible? You define setter and getter. You can do it like this:class K
def initialize(a)
@a=a
end
# this is the getter
def a
@a
end
# this is the setter, isn't that the most elegant syntax for a setter?
def a=(v)
@a=v
end
end
k=K::new(56)
puts k.a #=> 56
k.a="33"
puts k.a #=> "33"
class K
#...
attr_accessor :a,:b # trivial setter and getter for @a and @b
attr_reader :c # trivial getter and no setter for @c
attr_writer :d # just the setter (well, I never used this)
end
One last remark about fields: they are always defined. If you run a new irb console and write
xx
, it's going to say that the variable or method is not defined. But if you do this:class KK
def test_xx
puts @xx
end
end
KK::new.test_xx
nil
. Class attributes (that's the more ruby-like name for fields) are never undefined. They are just nil
.All for today, thanks for listening.
No comments:
Post a Comment