将父类的实例更改为子类

时间:2014-12-08 00:24:21

标签: ruby class

在下面的示例中,我有Pet类,它是Dog类的子类。我希望能够将Dog的实例转换为Pet

class Dog
  def bark
    "Wof!"
  end
  attr_accessor :age
end


class Pet < Dog
  attr_accessor :name
end


white_dog = Dog.new
puts white_dog.class  # => Pet
puts white_dog.bark   # => Wof!
white_dog.age = 5

假设我刚收养了这只狗。他是我的宠物,并且有一个名字。 我想将white_dogDog类更改为Pet类,所以我明白了:

puts white_dog.class          # => Pet
puts white_dog.bark           # => Wof!
puts white_dog.age = 5        # => 5
puts white_dog.name = "Rex"   # => Rex

是否可以在不使用Pet.new并传递所有实例变量(如@age)的情况下执行此操作?

2 个答案:

答案 0 :(得分:2)

我不相信你能做你想做的事,但我无法提供证据。如果您仍然需要创建一个Pets实例,其实例变量与给定Dog实例的值相同,那么这里至少可以自动复制实例值变量

<强>代码

def copy_instance_variables(parent, child)
  parent.instance_variables.each { |v| 
  child.instance_variable_set(v, parent.instance_variable_get(v)) }
end

示例

class Dog
  def favorites(food, toy)
    @food = food
    @toy = toy
  end
end

fifi = Dog.new
  #=> #<Dog:0x00000102089488>
fifi.favorites('roast beef', 'frisbee')
  #=> "frisbee"

class Pet < Dog
end

mimi = Pet.new
  #=> #<Pet:0x0000010207bb08>

copy_instance_variables(fifi, mimi)
  #=> [:@food, :@toy]

mimi
  #=> #<Pet:0x0000010207bb08 @food="roast beef", @toy="frisbee">

顺便说一下,Pet成为Dog的子类有点不寻常;人们会期望它是另一种方式。

<强>附录

不要因为没有提供你想要的所有内容而对Ruby太过刻苦,但另一方面,任何人都可以向Ruby僧侣提出一些补充新内容的案例。

优点是,由于Ruby具有开放类,因此您始终可以添加新方法来执行您想要的操作。在这里,您希望将类的实例“转换”为子类的实例。以下是添加方法的示例:

copy_instance_to_subclass(subclass)

到班级Object。此方法将用于创建给定子类的实例,并从父类实例self复制实例变量和类实例变量的值。但是,当父类和子类都没有initialize方法时,这只能起作用。 (考虑一下。创建一个类的实例而不调用它initialize是没有意义的。)同样,我可能还有其他限制。

如果A是父类,而AA是子类,则调用它:

a = A.new
aa = a.copy_instance_to_subclass(AA)

复制实例后,您可以将变量a设置为其他内容(例如nilaa),从而放弃它。这告诉垃圾收集器它可以被拖到垃圾场。

代码

class Object
  def copy_instance_to_subclass(subclass)
    isub = subclass.send :new 
    # copy values of instance variables
    instance_variables.each { |v| 
      isub.instance_variable_set(v, instance_variable_get(v)) }
    # copy values of class instance variables
    self.class.instance_variables.each { |v| 
      subclass.instance_variable_set(v, self.class.instance_variable_get(v)) }
    isub
  end
end

实施例

class A
  @@a  = 0
  @b   = 1
  def set_vars
    @c = 2
    @d = 3
  end
end

class AA < A
  def show_cv
    puts "@@a = #{@@a}"
  end
end

a = A.new
  #=> #<A:0x000001012b8020>
a.set_vars
  #=> 3
aa = a.copy_instance_to_subclass(AA)
  #=> #<AA:0x000001012c82b8 @c=2, @d=3>

检查实例变量

让我们确认aaAA的实例变量具有正确的值。首先,制作小型辅助方法非常有用:

def display_ivs(obj)
  obj.instance_variables.each { |v| puts "#{v} = #{obj.instance_variable_get(v)}" }
end

display_ivs(aa)
@c = 2
@d = 3
  #=> [:@c, :@d]

检查类实例变量

display_ivs(AA)
@b = 1
  #=> [:@b]

确认类变量可见

aa.show_cv
  # @@a = 0

答案 1 :(得分:1)

无法在Ruby中更改对象的类。

Smalltalk有become:方法,它允许一个对象成为另一个对象,并且由于任何对象都可以become:任何其他对象,理论上新对象可以有不同的类。但即使在这种极端情况下,您也无法更改现有对象的类,您可以将对象更改为具有(可能)不同类的不同对象