190 likes | 290 Views
Ruby Classes. and other languages… . Objects and Classes. What does that mean?. Every value in Ruby is (or behaves like) an object 2.times { puts “hey”} Objects are instances of classes Instance variables are strictly encapsulated. Object creation. def initialize(x, y, z=nil)
E N D
Ruby Classes and other languages…
Objects and Classes What does that mean? • Every value in Ruby is (or behaves like) an object • 2.times { puts “hey”} • Objects are instances of classes • Instance variables are strictly encapsulated
Object creation def initialize(x, y, z=nil) @x, @y, @z = x,y,z end • Every class inherits method new • calls allocateto get space (can’t override) • calls initializeto create instance variables • Often convenient to provide default parameters for initialize • Not covered: dup, clone, initialize_copy • Not covered: marshalling (create from serialized data)
Simple Class class Cat def initialize(name, age) @name, @age = name, age end def to_s "(#@name, #@age)" end def name @name end def age @age end def name=(value) @name=value end def age=(value) @age=value end end c = Cat.new("Fluffy", 2) puts c puts c.name c.age=5 puts c.age new calls initialize to_s like toString getters and setters, called attributes last expression in function is return @ indicates instance variable always refer to self DO NOT declare outside of method
Operator Overloading, etc. class Bottle attr_accessor :ounces attr_reader :label def initialize(label, ounces) @label = label @ounces = ounces end def add(other) Bottle.new(@label, @ounces+other.ounces) end def add!(other) @ounces += other.ounces other.ounces = 0 end def split(scalar) newOunces = @ounces / scalar @ounces -= newOunces Bottle.new(@label, newOunces) end def +(amount) Bottle.new(@label, @ounces + amount) end def to_s "(#@label, #@ounces)" end end b = Bottle.new("Tab", 16) puts b.label b.ounces = 20 puts "b is #{b}“ puts "result of b2 = b.split 4" b2 = b.split 4 puts "b is #{b}" puts "b2 is #{b2}“ puts "result of b3 = b.add(b2)" b3 = b.add(b2) puts "b3 is #{b3}“ puts "result of b.add!(b2)" b.add!(b2) puts "b is #{b}" puts "b2 is #{b2}“ puts "result of b4 = b3 + 10" b4 = b3 + 10 puts "b4 is #{b4}" attr is example of metaprogramming
Duck Typing class Can attr_accessor :ounces def initialize(label, ounces) @label = label @ounces = ounces end def to_s "(#@label, #@ounces)" end end b = Bottle.new("Tab", 16) c = Can.new(“Coke", 12) b2 = b.add(c) puts "result of b2 = b.add(c)" puts "b2 is #{b2}“ cat = Cat.new b3 = b.add(cat) • If it walks like a duck and quacks like a duck, it must be a duck • any object with needed method will work • undefined method ‘ounces’ for <Cat> http://beust.com/weblog/2005/04/15/the-perils-of-duck-typing/
Object Class Quick Ex: how does Java handle? x=1 x.instance_of? Fixnum => true x.instance_of? Numeric => false (even though subclass) x.is_a? Fixnum => true x.is_a? Numeric => true x.is_a? Comparable => true x.is_a? Object => true x.class == Fixnum => true x.class == Numeric => false
Object Type • Class of an object doesn’t change • Type is more fluid… includes set of behaviors (methods it responds to) • i.e, Class != Type….whoaaa • respond_to?
Object equality equal? checks addresses (like == in Java) for Object, == is synonym for equal? Most classes override == (like equal in Java)
Equality Example class Bottle attr_accessor :ounces attr_reader :label def initialize(label, ounces) @label = label @ounces = ounces end def ==(other) return false if ! other.instance_of? Bottle return @ounces == other.ounces && @label == other.label end alias eql? == end # Can also has ounces class Can attr_accessor :ounces def initialize(label, ounces) @label = label @ounces = ounces end end b = Bottle.new("Tab", 16) b2 = Bottle.new("Tab", 16) b3 = Bottle.new("Tab", 12) b4 = Bottle.new("Coke", 16) puts "b.equal?(b2) #{b.equal?(b2)}" puts "b == b2 #{b == b2}" puts "b == b3 #{b == b3}" puts "b == b4 #{b == b4}“ puts "b.eql?(b2) #{b.eql?(b2)}" puts "b.eql?(b3) #{b.eql?(b3)}" c = Can.new("Tab", 16) puts "b == c #{b == c}"
More object equality Other language with eql?/=== feature? • Numeric classes will do type conversions when used with== • eql? is like ==, but no type conversion • 1.eql? 1.0 • can alias eql? with == • === used with case statement • (1..10) === 5 # true, 5 is in range • /\d+/ === “123” # true, matches regex • String === “s” # true if s is instance of String
Type conversions • Widening Conversions: can include at least approximations to all of the values of the original type. Examples (Java/C++) • byte to short, int, long, float or double • short to int, long, float or double • char to int, long, float or double • int to long, float or double • long to float or double • float to double • Narrowing conversions: cannot include all of the values of the original type • short to byte or char • char to byte or short • int to byte, short, or char • long to byte, short, char or int • float to byte, short, char, int or long • double to byte, short, char, int, long or float
Type conversions – dangerous? Conversions should be used with care! Warnings should not just be ignored… Strongly typed language minimizes type conversions Even widening conversions may lose accuracy. Example: integers stored in 32 bits, 9 digits of precision. Floating point values also stored in 32 bits, only about 7 digits of precision (because of space used for exponent).
Type-safe Methods class Bottle attr_accessor :ounces attr_reader :label def initialize(label, ounces) @label = label @ounces = ounces end def add(other) raise TypeError, "Bottle argument expected " unless other.is_a? Bottle Bottle.new(@label, @ounces+other.ounces) end def add2(other) raise TypeError, "Bottle argument expected " unless other.respond_to? :ounces Bottle.new(@label, @ounces+other.ounces) end def add3(other) Bottle.new(@label, @ounces+other.ounces) rescue raise TypeError, "Cannot add with an argument that doesn't have ounces" end def to_s "(#@label, #@ounces)" end end class Can attr_accessor :ounces def initialize(label, ounces) @label = label @ounces = ounces end def to_s "(#@label, #@ounces)" end end class Cat end b = Bottle.new("Tab", 16) c = Can.new("Coke", 12) puts "result of b2 = b.add(c)" b2 = b.add(c) puts "result of b2 = b.add2(c)" b2 = b.add2(c) puts "b2 is #{b2}" puts "result of b2 = b.add3(c)" b2 = b.add3(c) puts "b2 is #{b2}" cat = Cat.new puts "result of b2 = b.add3(cat)" b2 = b.add3(cat)
More overloading class Bottle attr_accessor :ounces attr_reader :label def initialize(label, ounces) @label = label @ounces = ounces end def each yield @label yield @ounces end def [](index) case index when 0, -2 then @label when 1, -1 then @ounces when :label, "label" then @label when :ounces, "ounces" then @ounces end end end b = Bottle.new("Tab", 16) puts "using each" b.each {|x| puts x } puts "[ix]" puts b[0] puts b[-2] puts b[1] puts b[-1] puts "[field name]" puts b["label"] puts b[:label] puts b["ounces"] puts b[:ounces]
Object Comparisons Implement <=> operator Like compareTo (-1, 0, 1 and nil if not comparable) Typically include Comparable module as a mixin Provides <, <=, ==, >=, >(defined in terms of <=>) Sometimes write == anyway (e.g., one may be case-sensitive, may be more efficient). Best to be consistent.
Comparison example class Bottle include Comparable attr_accessor :ounces attr_reader :label def initialize(label, ounces) @label = label @ounces = ounces end def hash #based on Effective Java by Bloch code = 17 code = 37 * code + @label.hash code = 37 * code + @ounces.hash code end def <=>(other) return nil unless other.instance_of? Bottle @ounces <=> other.ounces end end b = Bottle.new("Tab", 16) b2 = Bottle.new("Tab", 12) b3 = Bottle.new("Coke", 16) b4 = Bottle.new("Tab", 16) puts "b == b2 #{b == b2}" puts "b < b2 #{b < b2}" puts "b > b2 #{b > b2}" puts "b == b3 #{b == b3}" puts "b == b4 #{b == b4}"
Class Methods and variables class Bottle include Comparable attr_accessor :ounces attr_reader :label MAX_OUNCES = 64 @@numBottles = 0 def initialize(label, ounces) @label = label @@numBottles += 1 if ounces > MAX_OUNCES @ounces = MAX_OUNCES else @ounces = ounces end end def Bottle.sum(bottles) total = 0 bottles.each {|b| total += b.ounces } total end def to_s "(#@label, #@ounces)" end def self.report #class method puts "Number of bottles created: #@@numBottles" end end bottles = Array.new bottles[0] = Bottle.new("Tab", 16) bottles[1] = Bottle.new("Tab", 16) bottles[2] = Bottle.new("Coke", 16) bottles[3] = Bottle.new("Tab", 20) puts "Total ounces: #{Bottle.sum bottles}" b = Bottle.new("Sprite", 72) puts b Bottle.report Not covered: Class Instance Variables
Topic Summary • Language Concepts • Classes • Instance variables • Object creation • Determine object type • Object equality • Object comparisons • Type conversions • Type safety • Class methods • Class variables • Ruby • new • to_s • duck typing