在下面的示例中,我有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_dog
从Dog
类更改为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
)的情况下执行此操作?
答案 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
设置为其他内容(例如nil
或aa
),从而放弃它。这告诉垃圾收集器它可以被拖到垃圾场。
代码的
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>
检查实例变量
让我们确认aa
和AA
的实例变量具有正确的值。首先,制作小型辅助方法非常有用:
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:
任何其他对象,理论上新对象可以有不同的类。但即使在这种极端情况下,您也无法更改现有对象的类,您可以将对象更改为具有(可能)不同类的不同对象