# Lua Inheritance #

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

--- This demonstrates a technique for implementing object oriented inheritance in Lua.
--- # Simple Classes
--- The following example implements a class with no inheritance:

---

SimpleClass = {}
SimpleClass_mt = { __index = SimpleClass }

-- This function creates a new instance of SimpleClass
function SimpleClass:create()
local new_inst = {}; -- the new instance
setmetatable(new_inst, SimpleClass_mt); -- all instance share the same metatable
return new_inst;
end

-- Here are some functions for SimpleClass
function SimpleClass:className()
print("SimpleClass")
end

function SimpleClass:doSomething()
print("Doing something")
end

---

--- In the above example, SimpleClass represents a table that holds all of our class's methods, like a class declaration.
--- SimpleClass_mt is the metatable we will attach to each class instance we create. The function "SimpleClass:create()"
--- creates an instance of our class SimpleClass. Construction of a class instance involves creating an empty table
--- and then attaching our SimpleClass metamethods to it. The result of attaching the metamethods is that the new
--- instance looks to the metatable we attached for its customised behaviour.
---
--- Method invocations(方法调用) on the instance will trigger the "index" event on the instance, causing a lookup on the "__index"
--- member of the instance's metatable. The __index member is simply a reference to SimpleClass. Therefore, method
--- invocations on the instance will cause a lookup in the SimpleClass table.
---

--- # Implementing Inheritance
--- Now we want to create a new class SubClass that inherits and, optionally, overrides functions from SimpleClass.

-- Create a new class that inherits from a base class
function inheritsFrom(baseClass)
-- The following lines are equivalent to the SimpleClass example:
-- Create the table and metatable representing the className
local new_class = {}
local class_mt = { __index = new_class }
-- Note that this function uses class_mt as an upvalue, so every instance of the class will share the same metatable.
function new_class:create()
local newinst = {}
setmetatable(newinst, class_mt)
return newinst
end
-- The following is the key to implementing inheritance:
-- The __index memeber of the new class's metatable references the base class. This implies that methods of the
-- base class will be exposed to the sub-class, and that the sub-class can override any of these methods.
if baseClass then
setmetatable(new_class, { __index = baseClass })
end
return new_class
end

--- The function inheritsFrom(baseClass) takes a single argument, the class declaration we want to inherit from. The
--- function returns a class declaration which we can then tailor. "new_class" is the new class declaration to be
--- returned. The nested function "new_class:create()" is part of the class declaration returned and will create
--- new instances of the sub class we are creating. This function creates a newinst table which uses our new class
--- table to hold its methods. The new class table in turn looks in the baseClass if it cannot find a method we require,
--- and thus we inherit its methods.
---

--- # Inheritance Example
--- Building on Simpleclass we now create a class called SubClass that inherits from SimpleClass and overrides className()
---

SubClass = inheritsFrom(SimpleClass) -- create a class that inherits from SimpleClass
function SubClass:className()
-- override className() function
print("SubClass")
end
simple = SimpleClass:create() -- create an instance of SimpleClass
simple:className()

--- # OO Properties
--- We can now expand on our inheritance structure and add features that are common in other languages, like access to
--- class's super class and a isa() method that provides type id functionality:
---

-- a new inheritsFrom() function
function inheritsFrom(baseClass)

local new_class = {}
local class_mt = { __index = new_class }

function new_class:create()
local newinst = {}
setmetatable(newinst, class_mt)
return newinst
end

if nil ~= baseClass then
setmetatable(new_class, { __index = baseClass })
end

-- implementation of additional OO properties starts here
-- return the class object of the instance
function new_class:class()
return new_class
end

--return the super class object of the instance
function new_class:superClass()
return baseClass
end

-- return true if the caller is an instance of theClass
function new_class:isa(theClass)
local b_isa = false
local cur_class = new_class
while (nil ~= cur_class) and (false == b_isa) do

if cur_class == theClass then
b_isa = true
else
cur_class = cur_class:superClass()
end
end
return b_isa
end
return new_class
end

SimpleClass = inheritsFrom(nil)
SubClass = inheritsFrom(SimpleClass)
FinalClass = inheritsFrom(SubClass)
sub = SubClass:create()
fc = FinalClass:create()
print(fc:isa(SubClass)) --> true
print(fc:isa(FinalClass)) --> true
print(sub:isa(SubClass)) --> true
print(sub:isa(FinalClass)) --> false

--- # Alternative Approach: Prototype Based
--- Prototype-based programming is a style of object-oriented programming in which classes are not present, and behaviour
--- reuse(known as inheritance in class-based language) is performed via a process of cloning existing objects that
--- serve as prototypes. This model can also be known as class-less, prototype-oriented or instance-based programming.
---
--- Most of the code is basically the same as above, but reduced to only the essentials needed to make "Prototype based
--- programming" work. More exactly it allows prototype programming using cloning and prototype delegation. Access to
--- a property not set in an object is delegated to it's prototype. This code uses the table as the very basic prototype
--- and object as a specialization of table. The function object.isa is not strictly needed for the prototype paradigm,
--- but more of a convenience.
---
--- Function :
--- --> "clone(base_object[, clone_object]) -> table"
--- Parameters :
--- base_object --> table, the base object to be cloned.
--- clone_object --> table, an optional object to turn into a clone of base_object.
--- Returns:
--- table --> the new clone
---
--- If "new_object" is not of type "table new_object" is returned if it's not nil which case "base_object" is return.
--- "new_object" has it's metatable set to itself, and it's "__index" now points to its prototype "base_object.clone" is
--- also available as "object.clone"
---
--- Function:
--- --> isa(clone_object, base_object) -> bool
--- Parameters:
--- clone_object --> table, the clone to check
--- base_object --> table, the suspected base of clone_object
--- Returns:
--- bool --> clone has base in the prototype hierarchy
---
--- If neither of the arguments is a table isa falls back to returning the comparison of the types. It's also av ailable
--- as object.isa.
---

-- The Code
function clone(base_object, clone_object)
if type(base_object) ~= "table" then
return clone_object or base_object
end
clone_object = clone_object or {}
clone_object.__index = base_object
return setmetatable(clone_object, clone_object)
end

function isa(clone_object, base_object)
local clone_object_type = type(clone_object)
local base_object_type = type(base_object)
if clone_object_type ~= "table" and base_object_type ~= "table" then
return clone_object_type == base_object_type
end
local index = clone_object.__index
local _isa = index == base_object
while not _isa and index ~= nil do
index = index.__index
_isa = index == base_object
end
return _isa
end

object = clone(table, { clone = clone, isa = isa })

--- end.