Единственная разница касается использования объявленных в классе констант. Если метод класса объявлен вне объявления самого класса, то эти константы оказываются вне области видимости. Например,
в первом фрагменте метод
detect_hardware
может напрямую обращаться к константе
MAX_SAMPLE
, а во втором придется пользоваться нотацией
SoundPlayer::MAX_SAMPLE
.
Не удивительно, что помимо методов класса есть еще и переменные класса. Их имена начинаются с двух знаков
@
, а областью видимости является весь класс, а не конкретный его экземпляр.
Традиционный пример использования переменных класса - подсчет числа его экземпляров. Но они могут применяться всегда, когда информации имеет смысл в контексте класса в целом, а не отдельного объекта. Другой пример приведен в листинге 11.3.
Листинг 11.3. Переменные и методы класса
class Metal
@@current_temp = 70
attr_accessor :atomic_number
def Metal.current_temp=(x)
@@current_temp = x
end
def Metal.current_temp
@@current_temp
end
def liquid?
@@current_temp >= @melting
end
def initialize(atnum, melt)
@atomic_number = atnum
@melting = melt
end
end
aluminum = Metal.new(13, 1236)
copper = Metal.new(29, 1982)
gold = Metal.new(79, 1948)
Metal.current_temp = 1600
puts aluminum.liquid? # true
puts copper.liquid? # false
puts gold.liquid? # false
Metal.current_temp = 2100
puts aluminum.liquid? # true
puts copper.liquid? # true
puts gold.liquid? # true
Здесь переменная класса инициализируется до того, как впервые используется в методе класса. Отметим также, что мы можем обратиться к переменной класса из метода экземпляра, но обратиться к переменной экземпляра из метода класса нельзя. Немного подумав, вы поймете, что так и должно быть.
А если попытаться, что произойдет? Что если мы попробуем напечатать атрибут
@atomic_number
из метода
Metal.current_temp
? Обнаружится, что переменная вроде бы существует — никакой ошибки не возникает, — но имеет значение
nil
.
В чем дело?
В том, что на самом деле мы обращаемся вовсе не к переменной экземпляра класса
Metal
, а к переменной экземпляра класса
Class
. (Напомним, что в Ruby
Class
— это класс!)
Мы столкнулись с переменной экземпляра класса (термин заимствован из языка Smalltalk). Дополнительные замечания на эту тему приводятся в разделе 11.2.4.
В листинге 11.4 иллюстрируются все аспекты этой ситуации.
Листинг 11.4. Данные класса и экземпляра
class MyClass
SOME_CONST = "alpha" # Константа уровня класса.
@@var = "beta" # Переменная класса.
@var = "gamma" # Переменная экземпляра класса.
def initialize
@var = "delta" # Переменная экземпляра.
end
def mymethod
puts SOME_CONST # (Константа класса.)
puts @@var # (Переменная класса.)
puts @var # (Переменная экземпляра.)
end
def MyClass.classmeth1
puts SOME_CONST # (Константа класса.)
puts @@var # (Переменная класса.)
puts @var # (Переменная экземпляра класса.)
end
end
def MyClass.classmeth2
puts MyClass::SOME_CONST # (Константа класса.)
# puts @@var # Ошибка: вне области видимости.
puts @var # (Переменная экземпляра класса.)
end
myobj = MyClass.new
MyClass.classmeth1 # alpha, beta, gamma
MyClass.classmeth2 # alpha, gamma
myobj.mymethod # alpha, beta, delta
Следует еще сказать, что метод класса можно сделать закрытым, воспользовавшись методом
private_class_method
. Это аналог метода
private
на уровне экземпляра. См. также раздел 11.2.10.
11.1.5. Наследование суперклассу
Можно унаследовать класс, воспользовавшись символом
<
:
class Boojum < Snark
# ...
end
Это объявление говорит, что класс
Boojum
является подклассом класса
Snark
или — что то же самое — класс
Snark
является суперклассом класса
Boojum
. Всем известно, что каждый буюм является снарком, но не каждый снарк — буюм.
Ясно, что цель наследования — расширить или специализировать функциональность. Мы хотим получить из общего нечто более специфическое.