Class 클래스
Class 클래스
Ruby에서 클래스는 일급 객체(first-class object)예요. 각 클래스는 Class 클래스의 인스턴스이죠.
출처: Ruby 3.3 API
본문
보통은 이렇게 새 클래스를 만들어요.
class Name
# some code describing the class behavior
end
새 클래스가 만들어지면 Class 타입의 객체가 초기화되어 전역 상수(여기선 Name)에 할당돼요.
Name.new로 새 객체를 만들면 기본적으로 Class의 new 메서드가 실행돼요. 이건 Class에서 new를 재정의해 확인할 수 있어요.
class Class
alias old_new new
def new(*args)
print "Creating a new ", self.name, "\n"
old_new(*args)
end
end
class Name
end
n = Name.new
이 코드는 이렇게 출력해요.
Creating a new Name
클래스, 모듈, 객체는 서로 밀접하게 연결돼 있어요. 아래 다이어그램에서 세로 화살표는 상속을, 괄호는 메타클래스를 나타내요. 모든 메타클래스는 Class 클래스의 인스턴스예요.
+---------+ +-...
| | |
BasicObject-----|-->(BasicObject)-------|-...
^ | ^ |
| | | |
Object---------|----->(Object)---------|-...
^ | ^ |
| | | |
+-------+ | +--------+ |
| | | | | |
| Module-|---------|--->(Module)-|-...
| ^ | | ^ |
| | | | | |
| Class-|---------|---->(Class)-|-...
| ^ | | ^ |
| +---+ | +----+
| |
obj--->OtherClass---------->(OtherClass)-----------...
Public Class Methods
new(super_class=Object) → a_class
new(super_class=Object) { |mod| ... } → a_class
주어진 슈퍼클래스(파라미터가 없으면 Object)로 새 익명(이름 없는) 클래스를 만들어요. 클래스 객체를 상수에 할당하면 클래스에 이름을 붙일 수 있어요.
블록이 주어지면 그 블록에 클래스 객체가 전달되고, class_eval처럼 클래스 컨텍스트에서 블록이 평가돼요.
fred = Class.new do
def meth1
"hello"
end
def meth2
"bye"
end
end
a = fred.new #=> #<#<Class:0x100381890>:0x100376b98>
a.meth1 #=> "hello"
a.meth2 #=> "bye"
일반 클래스처럼 다루고 싶다면 클래스를 상수(대문자 시작 이름)에 할당해요.
static VALUE
rb_class_initialize(int argc, VALUE *argv, VALUE klass)
{
VALUE super;
if (RCLASS_SUPER(klass) != 0 || klass == rb_cBasicObject) {
rb_raise(rb_eTypeError, "already initialized class");
}
if (rb_check_arity(argc, 0, 1) == 0) {
super = rb_cObject;
}
else {
super = argv[0];
rb_check_inheritable(super);
if (super != rb_cBasicObject && !RCLASS_SUPER(super)) {
rb_raise(rb_eTypeError, "can't inherit uninitialized class");
}
}
RCLASS_SET_SUPER(klass, super);
rb_make_metaclass(klass, RBASIC(super)->klass);
rb_class_inherited(super, klass);
rb_mod_initialize_exec(klass);
return klass;
}
Public Instance Methods
allocate() → obj
클래스의 새 객체 공간을 할당하되, 새 인스턴스에 initialize를 호출하지 않아요. 반환된 객체는 반드시 class의 인스턴스여야 해요.
klass = Class.new do
def initialize(*args)
@initialized = true
end
def initialized?
@initialized || false
end
end
klass.allocate.initialized? #=> false
static VALUE
rb_class_alloc_m(VALUE klass)
{
rb_alloc_func_t allocator = class_get_alloc_func(klass);
if (!rb_obj_respond_to(klass, rb_intern("allocate"), 1)) {
rb_raise(rb_eTypeError, "calling %"PRIsVALUE".allocate is prohibited",
klass);
}
return class_call_alloc_func(allocator, klass);
}
attached_object → object
리시버가 싱글턴 클래스인 대상 객체를 반환해요.
클래스가 싱글턴 클래스가 아니면 TypeError를 던져요.
class Foo; end
Foo.singleton_class.attached_object #=> Foo
Foo.attached_object #=> TypeError: `Foo' is not a singleton class
Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
TrueClass.attached_object #=> TypeError: `TrueClass' is not a singleton class
NilClass.attached_object #=> TypeError: `NilClass' is not a singleton class
VALUE
rb_class_attached_object(VALUE klass)
{
if (!FL_TEST(klass, FL_SINGLETON)) {
rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
}
return RCLASS_ATTACHED_OBJECT(klass);
}
json_creatable?()
이 클래스가 직렬화된 JSON 문자열에서 인스턴스 생성에 사용될 수 있으면 true를 반환해요. 클래스는 첫 파라미터로 해시를 기대하는 클래스 메서드 json_create를 구현해야 해요. 그 해시에는 필요한 데이터가 들어 있어야 해요.
# File ext/json/lib/json/common.rb, line 695
def json_creatable?
respond_to?(:json_create)
end
new(args, ...) → obj
클래스의 새 객체를 만들기 위해 allocate를 호출한 다음, 그 객체의 initialize 메서드를 args와 함께 호출해요. .new로 객체를 만들 때마다 결국 호출되는 메서드예요.
VALUE
rb_class_new_instance_pass_kw(int argc, const VALUE *argv, VALUE klass)
{
VALUE obj;
obj = rb_class_alloc(klass);
rb_obj_call_init_kw(obj, argc, argv, RB_PASS_CALLED_KEYWORDS);
return obj;
}
subclasses → array
리시버가 직접 슈퍼클래스인 클래스들의 배열을 반환해요. 싱글턴 클래스는 제외하고, 반환 배열의 순서는 정의되지 않아요.
class A; end
class B < A; end
class C < B; end
class D < A; end
A.subclasses #=> [D, B]
B.subclasses #=> [C]
C.subclasses #=> []
익명(상수와 연결되지 않은) 서브클래스도 반환돼요.
c = Class.new(A)
A.subclasses # => [#<Class:0x00007f003c77bd78>, D, B]
부모는 서브클래스에 대한 참조를 유지하지 않으므로 가비지 컬렉션을 막지 않아요. 즉, 서브클래스에 대한 모든 참조가 사라지면 서브클래스가 사라질 수 있어요.
# drop the reference to subclass, it can be garbage-collected now
c = nil
A.subclasses
# It can be
# => [#<Class:0x00007f003c77bd78>, D, B]
# ...or just
# => [D, B]
# ...depending on whether garbage collector was run
VALUE
rb_class_subclasses(VALUE klass)
{
return class_descendants(klass, true);
}
superclass → a_super_class or nil
클래스의 슈퍼클래스를 반환하거나, 없으면 nil을 반환해요.
File.superclass #=> IO
IO.superclass #=> Object
Object.superclass #=> BasicObject
class Foo; end
class Bar < Foo; end
Bar.superclass #=> Foo
주어진 클래스에 부모 클래스가 없으면 nil을 반환해요.
BasicObject.superclass #=> nil
VALUE
rb_class_superclass(VALUE klass)
{
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
VALUE super = RCLASS_SUPER(klass);
if (!super) {
if (klass == rb_cBasicObject) return Qnil;
rb_raise(rb_eTypeError, "uninitialized class");
}
if (!RCLASS_SUPERCLASS_DEPTH(klass)) {
return Qnil;
}
else {
super = RCLASS_SUPERCLASSES(klass)[RCLASS_SUPERCLASS_DEPTH(klass) - 1];
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
return super;
}
}
Private Instance Methods
inherited(subclass)
현재 클래스의 서브클래스가 만들어질 때마다 호출되는 콜백이에요.
예시:
class Foo
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
end
class Bar < Foo
end
class Baz < Bar
end
이 코드는 이렇게 출력해요.
New subclass: Bar
New subclass: Baz
#define rb_obj_class_inherited rb_obj_dummy1