Classes are a central concept in object-oriented programming. In Crystal, everything is an object, every object has a type, and it can respond to some methods. Classes are blueprints to create objects, providing initial values for state, like variables, and implementations of behavior, like methods. Objects are class instances that hold all the instance variables and states.
A class is defined using the class
keyword, followed by the name of the class written in UpperCamelCase
.
class Account
end
To create an instance of a class, call the class name using the new
method.
class Account
end
account = Account.new
In the example above the variable account
is an instance of the class Account
.
When creating an instance, a special method called initialize
acts as a constructor.
It is called when an instance is created.
The constructor allows you to set the initial state of the instance.
The initialize
method can take arguments, which are passed when creating an instance.
class Account
def initialize(number)
5 + number # => 9
end
end
Account.new(4)
The initialize
method cannot manually return a value, instead, new
returns the newly created instance.
class Account
def initialize(number)
5 + number
end
end
Account.new(4)
# => #<Account:0x7f5dc33dcea0>
An instance method is a method that is available to an instance of a class, and can be called on that instance.
Instance methods are defined using the def
keyword, followed by the method's name.
They are defined inside the class definition.
class Account
def balance
100
end
end
account = Account.new
account.balance # => 100
Instance variables are variables that are available in all methods of the instance.
To access an instance variable you need to define an instance method.
They can be initialized in the initialize
method.
They are defined using the @
prefix.
class Account
def initialize
@balance = 100
end
def balance
@balance
end
end
account = Account.new
account.balance
# => 100
Instance variables may also be defined inside the class definition.
class Account
@age = 0
end
Instance variables can be initialized with an argument passed to the initialize
method.
Since Crystal can't infer the type of argument used during initialization, it must be specified.
If you want to read more about this, you can read: type-inference.
To specify the type of the variable you can use the :
symbol, followed by the type.
For example, if you want to create an instance variable called balance
of type Int32
, you can do the following: @balance : Int32
.
Or if you want to create an instance variable called raining
of type Bool
, you can do the following: @raining : Bool
.
There are a few ways to implement this, either by in the initialize
method declaring the argument with the type, and then assigning the instance variable to the argument, like the following example:
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
Another way is to declare the instance variable type in the class definition in the form: @<variable> : <Type>
, like the following example:
class Weather
@raining : Bool
def initialize(raining)
@raining = raining
end
def raining
@raining
end
end
weather = Weather.new(true)
weather.raining
# => true
Methods can modify instance variables. These methods can be called on the instance of the class. When a method modifies an instance variable, that change is only available in the instance of the class you called it on.
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
Class methods are methods that are defined for a class but not for its instances.
They offer a way to create methods not dependent on the instance's state.
They are defined using the def
keyword, followed by self.<method name>
.
self
is a reference to the namespace which self is being called from.
In this case, it refers to the class, Account
.
It would be the same as if you would have written Account.
, but self.
is the preferred way.
class Account
def self.balance
100
end
end
Account.balance
# => 100
Your friend Johannes loves doing DIY (do it yourself) hardware. They just built a juice-maker, Johannes is very excited about it. Johannes's goal is to be able to make fresh juice every morning.
Johannes Juice maker has a lot of fancy features. Like a built-in measurement system, which can tell you how much juice is in the cup. It also has a timer, telling you how long the machine has been running.
Johannes isn't the best at programming, but they want to make a program to control the machine. They have thereby asked you to help them.
The machine has a debug light, it is a simple mechanic, if the machine has power the debug light is on. The program will only run if the debug light is on.
This method should be a class method called JuiceMaker
.
Define the class method JuiceMaker.debug_light_on?
that returns true
.
JuiceMaker.debug_light_on?
#=> true
When the machine is powered on it should create an instance of a class called JuiceMaker
.
The machine is supposed to have an initialized state.
The initialized state should hold the following information:
false
when the machine is turned on.Define the initialized state, which takes the amount of juice in the cup as a parameter, and construct the initialized state.
The initialized state should host an instance variable @running
that is set to false
, and an instance variable @fluid
that is based on an argument given when the class is initialized.
JuiceMaker.new(5)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=5>
The machine can be turned on and off.
You need to define a method to turn on the machine.
When the machine is turned on, the running state should be set to true
.
Define the method JuiceMaker#start
that turns the machine on.
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=true, @fluid=5>
The machine needs to be able to tell you the status of the machine.
It should return true
if the machine is running, and false
if the machine is not running.
Define the method JuiceMaker#running?
that returns the machine's status.
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker.running?
#=> true
The machine can add juice to the cup. The machine can tell how much juice is added to the cup. The machine needs help to know how much juice is in the cup after the juice is added.
Define the method JuiceMaker#add_fluid
, which takes the amount of juice added as a parameter and updates the amount of juice in the cup.
juice_maker = JuiceMaker.new(5)
juice_maker.add_fluid(5)
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=10>
The machine can be turned on and off.
You need to define a method to turn off the machine
This method will only be called when the machine is running.
When the machine is turned off, the running state should be set to false
.
The machine also needs help determining how much juice is in the cup after it is turned off.
The machine uses five units of juice per minute.
Define the method JuiceMaker#stop
that takes the number of minutes the machine has been running as a parameter and returns the amount of juice in the cup after the machine has been turned off.
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>
Sign up to Exercism to learn and master Crystal with 26 concepts, 133 exercises, and real human mentoring, all for free.