`

class 與 instance

    博客分类:
  • ruby
阅读更多
不知道為何,最近不管是私事或公事似乎都很多。造成的結果就是很多事不知道該從何處著手比較好。也許短時間內沒辦法寫什麼新東西也說不定,所以我又拿舊文章出來改了。這次講的是 Ruby 的物件系統。不過呢,由於這篇原本是回答別人的問題,因此有些地方講得不是很齊全,有些也講得比較偏。找到時間後,會再重新整理一次,現在就先這樣吧

編輯筆記:後面一段無關的刪去了,並補充了一小段。

==

在看 Ruby 的 class 與 instance 之前,先來看所謂 prototype-based language 是怎麼樣的東西,當然是舉大家最耳熟能詳的例子,ECMAScript(即 Javascript)

所謂 prototype-based 的意思是,沒有 class 的概念,所有的一切都是 instance, 產生東西一律使用 clone 的手法,從 prototype clone 出來。

在 ECMAScript 裡,要這樣操作 prototype:

=begin 例子

// 產生一個 function object, 會輸出「我是 ooo」
function say(){ print('I am ' + this) }

// 產生一個 function object, 拿這當 Duck 的 prototype
function Duck(){}

// 讓 Duck prototype 產生一個成員,也就是讓 say 變成他的 method
Duck.prototype.say = say;

// 定義 toString 讓 say 使用
Duck.prototype.toString = function(){ return 'Duck' }

// 假設現在有一個讓某東西說話的 function
function say_hello(who){ who.say() }

// 於是我們可以這樣呼叫 say_hello
say_hello(new Duck)

=end 例子

new Duck 會去尋找 Duck 的 prototype, 然後 clone 一份該 prototype 後傳回。所以 say_hello 的 who 會是一份 Duck 的複製,執行 say 則會輸出:

I am Duck



ok, 回到 Ruby. 雖然說 Ruby 被分類成 class-based, 但事實上,everything(ok, almost) is an object in Ruby, 就算是 class, 他其實本質上也是某個 instance, 是 Class 的 instance.

class A; end
a = A.new

a 是 A 的 instance, 所以 a 的 class 是 A.

a.class # A

A 是 Class 的 instance, 所以 A 的 class 是 Class

A.class # Class

其實,我覺得可以把這個 Class 視為某種 meta-class, 即 class 的 class, 如果我們要把 A 當嚴格 class 的話。但如果我們依然把 class 當 instance 看的話,當然,Class 本身其實也是一個 instance, 他是他自己的 instance.

Class.class # Class

有趣的是,這樣寫的話:

Class.object_id == Class.class.object_id

答案是:true.
Class.class 傳回來的,其實就是 Class, 也就是,他是他自己的 instance. 換句話說,其實 Class 是所有的 class 的 class.

A.kind_of? Class # true
Class.kind_of? Class # true
A.class # Class
Class.class # Class
A.class.object_id == Class.object_id # true



(btw, 其實更妙的是:

Module.kind_of? Class # true # Module 是 Class 的 instance
Class.kind_of? Module # true # Module 是 Class 的 superclass
Module.new.kind_of? Class # false # Module 的 instance 不是 Class



再加上 Object 會更複雜,可以試著畫畫看物件結構)

但是回想一下,一般我們是怎麼定義 class 的?

class A; end

其實,我個人會說這是一種 syntax sugar, 因為更合於 Ruby object system 的定義方式,應該是這樣:

A = Class.new
A.send(:define_method,
:say_hello,
lambda{ puts "Hello from A's instance." })

a = A.new
a.say_hello # Hello from A's instance.



由於 define_method 是 private 的,所以要用 send 去呼叫。其第一參數是你所要回應的 message symbol, 第二參數是 Proc/Method/Block 都可,將會成為該 method 的 body.

也就是說,其實你寫

class A
def say_hello
puts "Hello from A's instance."
end
end



對於 A 來說,他是先從 Class 產生一個實體(instance),然後將 A 這個「常數」指向那個實體,再對 A 呼叫 define_method, 把 say_hello 變成 symbol, 將 Block 變成該 method 的 body.

哪一個比較容易寫?當然是後者,畢竟那是大家都很習慣的模式,簡潔易懂。所以我會說那種寫法其實在某種程度上來說,是 syntax sugar... 而 Ruby 其實也是用 prototype 建出其 class 體系,這樣應該算是 prototype-based 還是 class-based, 看倌認為哩?

2007.02.08

補充:

有在寫 Ruby 的人,應該都知道 class 的名稱一定要大寫開頭。事實上,這只是一種延伸規則,並不是基本的規則。依照:

a = Class.new

仍然是可以得到一個不是大寫開頭的 class. 大寫開頭的 identifier 在 Ruby 是一個更簡單的概念:「常數」(constant)。也就是說,其實每一個 class name 都是一個常數,一個 constant variable refer to a Class's instance, 一個指向 Class 實體的常數,就跟一般的變數沒兩樣。所以我們可以寫:

def make klass
klass.new
end

這個 functiona 接受一個參數,這個參數的 type 的必要條件是實作 new method, 而任何一個 Class instance 都符合這個條件。

make(Array).push(123)

把 Array 這個 instance 丟給 make 這個 function, 回傳就是一個 Array instance.(global function 也值得探討,不過還是留待下次吧 :p)

那麼為什麼要讓 Class instance 成為一個常數?其實這是很顯而易見的,如果你把原本定義好的 class 整個變成其他人了,例如:

Array = String

這樣意義在哪裡?以後寫 Array.new 產生的就不是原本的 array, 而是 string 了。所以讓 class 成為一個 constant, 並要求其開頭字母是大寫,並非單純只是為了命名習慣的問題,背後是還有牽扯到其他因素的。

我覺得 Ruby 有趣且厲害並迷人的地方就在於,他不單單只是有強大的威力在內,還額外提供了許許多多方便使用的 syntax sugar, 使任何人都能快速上手,也能快速做到任何他想做到的事情。而當你需要更強大的表達能力時,也能立刻拋棄 syntax sugar, 從最原始的 Ruby 道理開始寫起程式。也就是說,Ruby 提供了各種不同的寫程式角度,大家都可以去找他喜愛的角度去使用。個人認為,Lisp 會不及 Ruby 方便,最主要的差別就在這裡了。(當然,我並不熟 Lisp, 所以這句話恐怕是大有問題在…。)

2007.03.27 godfat 真常

延伸閱讀
分享到:
评论

相关推荐

    java_innerclass_instance.rar_innerclass

    在“java_innerclass_instance.rar_innerclass”这个压缩包中,包含的资源可能是一个关于Java内部类实例应用的示例代码。 内部类主要有四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。这里我们主要...

    class literal & instance.getClass() & Class.forName(String className)

    在Java编程语言中,"class literal"、"instance.getClass()" 和 "Class.forName(String className)" 是三个与类加载和类型查询紧密相关的概念。了解这些概念对于深入理解Java运行时的类加载机制至关重要。 首先,让...

    Single Instance of an Application Class (21KB)

    标题“Single Instance of an Application Class (21KB)”和描述中的信息表明,这是一个关于如何确保应用程序仅有一个实例运行的技术主题。在计算机编程中,特别是Windows桌面应用开发中,"单例应用"是一种设计模式...

    Classfor的用法

    `Class.forName` 经常与 `newInstance` 方法配合使用,以实现动态创建对象的目的。`newInstance` 方法用于创建 `Class` 对象所表示的类的实例。 **示例**: ```java try { Class<?> clazz = Class.forName(...

    An MFC class to implement single instance apps(17KB)

    标题中的“An MFC class to implement single instance apps”是指一个基于Microsoft Foundation Class (MFC) 库的类,它的设计目的是确保应用程序只能运行一个实例。在Windows编程中,有时我们希望确保用户只能打开...

    new关键字和newInstance() 的区分

    而 newInstance() 方法则是 Class类的一个方法,用于创建一个新的对象。例如,`A a = (A) Class.forName("pacage.A").newInstance();` 就是使用 newInstance() 方法创建了一个 A 类的对象。newInstance() 方法的作用...

    对Python中class和instance以及self的用法详解

    在Python编程语言中,面向对象编程是一种重要的编程范式,其中,类(class)和实例(instance)是核心概念,而self则是用于引用实例自身的特殊参数。本文将深入讲解Python中class、instance以及self的用法。 一、...

    Instance inner class 3

    Instance inner 实例内部类 马克-to-win java视频的介绍

    VB.RichEdit.wrapper.class.instance..rar_richedit_richedit vb_vb

    在给定的压缩包文件中,"VB.RichEdit.wrapper.class.instance.."可能是关于一个VB版本的`RichEdit`封装类,这个类可能用于简化对`RichEdit`控件的操作,提高代码的可维护性和复用性。 `RichEdit`封装类通常包含以下...

    Class.forName()_newInstance()_New_三者区别

    在Java编程中,`Class.forName()`, `newInstance()` 和 `new` 关键字都是与对象创建和类加载紧密相关的概念,但它们各自有着不同的用途和限制。 首先,`Class.forName("className")` 是一个静态方法,它用于在运行...

    Class-Weighted Convolutional Features for Visual Instance Search.pdf

    我们利用类激活图(Class Activation Maps, CAMs)来获取图像中最具判别性的区域。CAMs是基于网络内部知识生成的,因此我们的方法无需外部信息,具有一定的优势。此外,我们还利用CAMs在初步快速搜索后的无监督再...

    Instance of Subclass, Subclass, Instance of Superclass, Superclass,

    在编程领域,尤其是在面向对象编程(OOP)中,"Instance of Subclass" 和 "Instance of Superclass" 是两个核心概念,它们与继承、多态等特性紧密相关。"Subclass"(子类)和 "Superclass"(超类或父类)是描述类...

    A Single-Instance Application Class(54KB)

    标题"A Single-Instance Application Class"指的是一个只允许一个实例运行的应用程序类。在计算机编程中,尤其是Windows桌面应用程序开发中,有时我们希望确保一个应用程序只能打开一个实例,防止用户无意或有意地多...

    newInstance()方法和new关键字

    ### newInstance()方法与new关键字的区别 在Java编程语言中,创建对象是十分常见的操作,而`newInstance()`方法和`new`关键字则是实现这一目的的两种主要方式。它们之间的区别不仅在于一个是方法调用,另一个是语言...

    C_Polymorphism_instance.rar_The Class

    public class DerivedClass : BaseClass { public DerivedClass(int value) : base(value) { // 这里可以添加子类的初始化逻辑 } } ``` 在这个例子中,`DerivedClass`的构造函数通过`base(value)`调用了基类`...

    浅谈thinkphp5 instance 的简单实现

    return TestClass::instance($option); } ``` 通过这个助手函数,我们可以轻松地获取`TestClass`的实例,并对其进行操作: ```php $app1 = app(); $app1->key = "Application 1"; $app2 = app(); ``` 在上述代码...

    C++ convenience class to communicate with a Matlab instance..zip

    标题 "C++ convenience class to communicate with a Matlab instance" 指的是一个C++类库,设计用于方便地与运行中的MATLAB实例进行交互。这样的工具通常允许开发者在C++程序中调用MATLAB的功能,执行计算,或者...

    详解Ruby中的instance_eval方法及其与class_eval的对比

    ### Ruby中的instance_eval方法及其与class_eval的对比 #### 一、instance_eval方法详解 **instance_eval** 方法属于Ruby语言中的eval家族成员之一,它的主要功能是将一个代码块(通常是一个包含Ruby代码的字符串...

    Java中的CLASS类Java中的CLASS类

    10.泛型与Class对象 泛型在Java中提供了类型安全,但编译后的字节码并不包含泛型信息。因此,即使在泛型类中,获取到的Class对象仍然不包含泛型信息。例如,`List<String>.class`实际上是`List.class`,这意味着在...

    jvm 加载class文件

    #### 一、Java与JVM中的Class文件加载机制概述 Java作为一种动态性极强的解释型编程语言,在程序运行时,Java虚拟机(JVM)负责将编译生成的`.class`文件加载到内存中进行执行。在Java环境中,每个类(Class)以及...

Global site tag (gtag.js) - Google Analytics