虛擬與抽象類型¶
當變數的類型在同一個類別階層下結合不同類型時,其類型會變成虛擬類型。這適用於除了 Reference
、Value
、Int
和 Float
之外的所有類別和結構。一個範例:
class Animal
end
class Dog < Animal
def talk
"Woof!"
end
end
class Cat < Animal
def talk
"Miau"
end
end
class Person
getter pet
def initialize(@name : String, @pet : Animal)
end
end
john = Person.new "John", Dog.new
peter = Person.new "Peter", Cat.new
如果您使用 tool hierarchy
命令編譯上述程式,您會看到 Person
的輸出如下:
- class Object
|
+- class Reference
|
+- class Person
@name : String
@pet : Animal+
您可以看到 @pet
是 Animal+
。 +
表示它是虛擬類型,意思是「任何繼承自 Animal
的類別,包括 Animal
」。
如果類型聯合在同一個階層下,編譯器總是會將其解析為虛擬類型
if some_condition
pet = Dog.new
else
pet = Cat.new
end
# pet : Animal+
編譯器對於相同階層下的類別和結構總是會這樣做:它會找到所有類型都繼承自的第一個超類別(不包括 Reference
、Value
、Int
和 Float
)。如果找不到,則類型聯合會保持不變。
編譯器這樣做的真正原因是為了能夠更快地編譯程式,而不是建立各種不同的相似聯合,同時也讓產生的程式碼體積更小。但另一方面,這也是合理的:同一個階層下的類別應該以類似的方式運作。
讓我們讓 John 的寵物說話:
john.pet.talk # Error: undefined method 'talk' for Animal
我們會收到一個錯誤,因為編譯器現在將 @pet
視為 Animal+
,其中包含 Animal
。而且由於它找不到 talk
方法,因此會發生錯誤。
編譯器不知道的是,對我們來說,永遠不會實例化 Animal
,因為實例化一個 Animal
沒有意義。我們可以透過將類別標記為 abstract
來告訴編譯器。
abstract class Animal
end
現在程式碼可以編譯了。
john.pet.talk # => "Woof!"
將類別標記為抽象也會阻止我們建立它的實例。
Animal.new # Error: can't instantiate abstract class Animal
為了更明確地表示 Animal
必須定義 talk
方法,我們可以將其作為抽象方法新增至 Animal
。
abstract class Animal
# Makes this animal talk
abstract def talk
end
透過將方法標記為 abstract
,編譯器會檢查所有子類別是否都實作此方法(符合參數類型和名稱),即使程式碼未使用它們。
抽象方法也可以在模組中定義,並且編譯器會檢查包含的類型是否實作它們。