类是面向对象编程中的核心概念。 在 Crystal 中,一切都是对象,每个对象都有类型,并且能响应某些方法。 类是创建对象的蓝图,它为状态(例如变量)提供初始值,并提供行为的实现(例如方法)。 对象是类的实例,它持有所有实例变量和状态。
使用 class 关键字定义类,后面跟上用 UpperCamelCase 写成的类名。
class Account
end
要创建类的实例,可以对类名调用 new 方法。
class Account
end
account = Account.new
在上面的例子中,变量account是类Account的一个实例。
创建实例时,一个名为 initialize 的特殊方法充当构造函数。
实例被创建时就会调用它。
构造函数让你可以设置实例的初始状态。
initialize 方法可以接收实参,这些实参在创建实例时传入。
class Account
def initialize(number)
5 + number # => 9
end
end
Account.new(4)
initialize 方法不能手动返回值,取而代之的是,new 会返回新建的实例。
class Account
def initialize(number)
5 + number
end
end
Account.new(4)
# => #<Account:0x7f5dc33dcea0>
实例方法是类的实例可以使用的方法,可以在该实例上调用。
实例方法使用 def 关键字定义,后面跟上方法名。
它们定义在类定义的内部。
class Account
def balance
100
end
end
account = Account.new
account.balance # => 100
实例变量是在实例的所有方法中都可以使用的变量。
要访问实例变量,你需要定义一个实例方法。
它们可以在 initialize 方法中初始化。
它们使用 @ 前缀定义。
class Account
def initialize
@balance = 100
end
def balance
@balance
end
end
account = Account.new
account.balance
# => 100
实例变量也可以定义在类定义内部。
class Account
@age = 0
end
实例变量可以用传给 initialize 方法的实参来初始化。
由于 Crystal 无法推断初始化时所用实参的类型,因此必须显式指定类型。
如果你想了解更多,可以阅读:类型推断。
要指定变量的类型,可以使用 : 符号,后面跟上类型。
例如,如果你想创建一个名为 balance、类型为 Int32 的实例变量,可以这样写:@balance : Int32。
或者,如果你想创建一个名为 raining、类型为 Bool 的实例变量,可以这样写:@raining : Bool。
有几种实现方式。一种是在 initialize 方法中声明带类型的实参,然后把实例变量赋值为该实参,如下例所示:
class Account
def initialize(balance : Int32)
@balance = balance
end
def balance
@balance
end
end
account = Account.new(1)
account_2 = Account.new(5)
account.balance
# => 1
account_2.balance
# => 5
另一种方式是在类定义中声明实例变量的类型,形式为 @<variable> : <Type>,如下例所示:
class Weather
@raining : Bool
def initialize(raining)
@raining = raining
end
def raining
@raining
end
end
weather = Weather.new(true)
weather.raining
# => true
方法可以修改实例变量。 这些方法可以在类的实例上调用。 当方法修改实例变量时,这一改动只在你调用它的那个实例中生效。
class Account
def initialize(balance : Int32)
@balance = balance
end
def withdraw(amount)
@balance -= amount
end
def balance
@balance
end
end
account = Account.new(10)
account_2 = Account.new(10)
account.balance
# => 10
account_2.balance
# => 10
account.withdraw(5)
account.balance
# => 5
account_2.balance
# => 10
类方法是为类而非类的实例定义的方法。
它们让你可以创建不依赖实例状态的方法。
它们使用 def 关键字定义,后面跟上 self.<method name>。
self 是对调用 self 时所处的命名空间的引用。
在这里,它指的是类 Account。
这与你写成 Account. 是一样的,但 self. 是更推荐的方式。
class Account
def self.balance
100
end
end
Account.balance
# => 100
你的朋友 Johannes 热爱 DIY 硬件,也就是自己动手做的硬件。 他们刚做了一台榨汁机,Johannes 为此兴奋不已。 Johannes 的目标是每天早上都能做出新鲜的果汁。
Johannes 的榨汁机有很多花哨的功能。 比如内置的测量系统,可以告诉你杯子里有多少果汁。 它还有一个计时器,告诉你机器已经运行了多久。
Johannes 不太擅长编程,但想写一个程序来控制这台机器。 因此,他们请你来帮忙。
机器上有一个调试灯,原理很简单:只要机器通电,调试灯就会亮。 只有调试灯亮着,程序才会运行。
这个方法应该是JuiceMaker类的一个类方法。
定义类方法JuiceMaker.debug_light_on?,让它返回true。
JuiceMaker.debug_light_on?
#=> true
机器通电时,应该创建一个名为JuiceMaker的类的实例。
机器应该有一个初始化状态。
初始化状态应该保存以下信息:
false。定义初始化状态,它接收杯中果汁的量作为形参,并构造这个初始化状态。
初始化状态应该包含一个设置为false的实例变量@running,以及一个实例变量@fluid,它的值基于初始化该类时传入的实参。
JuiceMaker.new(5)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=5>
机器可以开启和关闭。
你需要定义一个方法来开启机器。
机器开启时,运行状态应该设置为true。
定义方法JuiceMaker#start,用来开启机器。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=true, @fluid=5>
机器需要能够告诉你它的状态。
如果机器正在运行,它应该返回true;如果没有运行,则返回false。
定义方法JuiceMaker#running?,返回机器的状态。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker.running?
#=> true
机器可以把果汁加入杯子。 机器能知道往杯子里加了多少果汁。 加入果汁后,机器需要一些帮助才能知道杯子里有多少果汁。
定义方法JuiceMaker#add_fluid,它接收加入的果汁量作为形参,并更新杯中果汁的量。
juice_maker = JuiceMaker.new(5)
juice_maker.add_fluid(5)
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=10>
机器可以开启和关闭。
你需要定义一个方法来关闭机器。
这个方法只会在机器运行时被调用。
机器关闭时,运行状态应该设置为false。
机器还需要一些帮助,才能确定关闭后杯子里有多少果汁。
机器每分钟消耗 5 个单位的果汁。
定义方法JuiceMaker#stop,它接收机器已运行的分钟数作为形参,并返回机器关闭后杯中果汁的量。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker
#=> <JuiceMaker:0x10f0b8 @running=true, @fluid=5>
juice_maker.stop(1)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=0>