理解LUA中的多重继承

有人在问如何实现Lua中的多重继承。真正用到多重继承的时候,我会想想是不是非得用多重继承,总觉得多重继承有点“重”。

多重继承例子

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

local function search(k, tables)

fori, v in ipairs(tables)do

ifv[k] then

returnv[k]

end

end

returnnil

end

-- 这里实现多重继承,参数arg为多重父类表

function createClassFrom(...)

-- c为返回值的目标表,为实现多重继承的子类表

local c = {}

local parents = {...}

setmetatable(c, {__index = function(t, k)

returnsearch(k, parents)

end})

function c:new(o)

o = o or {}

setmetatable(o, {__index = c})

returno

end

returnc

end

-- 人 吃饭

Human = {name ="human"}

function Human:eat()

print("human eat")

end

-- 程序员 写代码

Programmer = {name ="coder"}

function Programmer:doProgramming()

print("do coding")

end

-- 女程序员 继承 人和程序员

-- 性别女

FemaleProgrammer = createClassFrom(Human, Programmer)

local femaleCoder = FemaleProgrammer:new({sex ="female", canBear = function() print("Female: can give birth a baby!") end})

femaleCoder:eat() -- human eat

femaleCoder:doProgramming() --docoding

上面代码难点在于理解lua中__index的用法。我们在lua中实现继承的时候,会用到这个__index。我们再次看看这个__index究竟是怎么回事。元表的__index可以是一个表,也可以是一个函数。

1. __index是个表的情况

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

local Test = { group ="quick"}

function Test:new(o)

o = o or {}

setmetatable(o, {__index = Test})

returno

end

function Test:getName()

returnself.name

end

function Test:setName(name)

self.name = name

end

local a = Test:new({name ="Just a test"})

print(a:getName()) -- Just a test

print(a.group) -- quick

当表a调用自身所没有的方法( getName() )或者属性(group)的时候, Lua会通过getmetatable(a)得到a的元表{index = Test}, 而该元表的`index`是个表Test,则Lua会在这个表Test中看看是否有缺少的域方法("getName")以及属性(group),如果找到了则会调用表Test的方法或者属性。

2. __index 是函数的情况

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

local Test = { }

Test.prototype = { group ="quick",

qq ="284148017",

company ="chukong",

getName = function()return"Just a test"end}

function Test:new(o)

o = o or {}

setmetatable(o, {__index = function(table, key)

returnTest.prototype[key]

end})

returno

end

local a = Test:new()

print(a:getName()) -- Just a test

print(a.qq) -- 284148017

print(a.company) -- chukong

当表a调用自身所没有的方法(getName)或者属性(qq/company)的时候, lua会通过getmetatable(a)得到a的元表

{__index = function(table, key) return Test.prototype[key] end}, 而该元表的__index是个函数,该函数的实参依次为正在调用方法、属性的表a以及表a中缺失的方法名或属性(键值key),lua会将这两个实参传入进去并调用__index指向的函数。

例如:

  • a:getName()时,就会调用a的元表的__index方法,将自身a以及"getName"键名依次传入进去,返回了Test.prototype["getName"]该函数,lua会自动调用该函数,所以a:getName()会返回Just a test。
  • a.qq时,就会调用a的元表的__index方法,将自身a以及"qq"键名依次传入进去,返回了Test.prototype["qq"]即284148017。

相关阅读