如何使用 Lua 让类 B 继承类 A?

我知道如何创建一个类 A:

A =
{
  x = 0,
  y = 0,
}

setmetatable(A, {
  __call = function(self, instance)
    return setmetatable(instance, { __index = A })
  end
})

function A:foo1()
  return string.format('Position(%d, %d)', self.x, self.y)
end

local a = A{ x = 7 } -- y will be 0 from class A
print(a.x) -- Prints 7
print(a.y) -- Prints 0

但我不知道如何让类 B 继承类 A。我希望能够在类 B 中进行自定义修改,但同时获得类 A 的所有信息。

我正在制作一个名为“战场”的事件系统,它就是类 A。我希望使用它来创建一些小游戏,比如“炸弹人”(它将是继承类 A 的类 B)、“吃豆人”(它将是继承类 A 的类 C)等等。我想要获得“战场”类的所有信息,以便在“炸弹人”和“吃豆人”中进行自定义修改,但两者之间没有关联。

例如:

B = -- B 继承自 A
{
  kills = 0,
}
-- 因此 B 类会有:x、y、kills

C = -- C 继承自 A
{
  deaths = 0,
}
-- 因此 C 类会有:x、y、deaths

class B/C inherits class A

点赞
用户2858170
用户2858170

继承是使用 __index 实现的。

请参见 https://www.lua.org/manual/5.4/manual.html#2.4

__index: 用于索引访问操作 table\[key\]。当 table 不是一个表或者找不到 key 时,就会出发这个事件。这个元值在 table 的元表中查找。这个事件的元值可以是函数、表或者带有 __index 元值的任何值。如果它是一个函数,它就会被调用,并将 tablekey 作为参数。这个调用的结果(调整为一个值)就是操作的结果。否则,最终结果就是使用 key 索引这个元值的结果。这个索引是正常的,不是原始的,因此可以触发另一个 __index 元值。

因此,每当 B 中没有实现某些功能时,您可以将索引操作引用到 A 上。

我建议您阅读一下这篇文章

http://lua-users.org/wiki/InheritanceTutorial

你可以将所有东西归结为这样的一个函数:

function CreateClass(...)
  -- "cls" 是新的类
  local cls, bases = {}, {...}
  -- 将基类的内容复制到新类中
  for i, base in ipairs(bases) do
    for k, v in pairs(base) do
      cls[k] = v
    end
  end
  -- 设置类的 __index,并开始填充一个 "is_a" 表,其中包含这个类及其所有基类,
  -- 这样可以使用 my_instance.is_a[MyClass] 进行“实例的”检查
  cls.__index, cls.is_a = cls, {[cls] = true}
  for i, base in ipairs(bases) do
    for c in pairs(base.is_a) do
      cls.is_a[c] = true
    end
    cls.is_a[base] = true
  end
  -- 类的 __call 元方法
  setmetatable(cls, {__call = function (c, ...)
    local instance = setmetatable({}, c)
    -- 如果有 init 方法,则运行它
    local init = instance._init
    if init then init(instance, ...) end
    return instance
  end})
  -- 返回新的类表,准备填充方法
  return cls
end

如果您理解了这段代码,您就应该知道所有必要的东西了。

2021-02-13 10:00:53
用户8312196
用户8312196

我和我的朋友Kamenuvol一起研究了这个问题。

我们在寻找可读性好且易于维护的解决方案。

基于我们从Piglet的答案中所理解的,这就是最终结果:

Inheritance

function setClass(class, parentClass) -- (class[, parentClass])
  -- class.__className is required
  assert(class.__className, "Parameter '__className' is required.")

  -- Set class metatable
  setmetatable(class, {
    __index = parentClass,
    __call = function(self, instance)
      return setmetatable(instance, { __index = class })
    end
  })

  -- Attach 'is' function
  class[string.format('is%s', class.__className)] = true
end

A =
{
  __className = 'A',

  x = 0,
  y = 0,
}

setClass(A)

function A:foo1()
  return string.format('Position(%d, %d)', self.x, self.y)
end

B =
{
  __className = 'B',

  kills = 0,
}

setClass(B, A)

function B:foo2()
  return string.format('Kills(%d)', self.kills)
end

C =
{
  __className = 'C',

  deaths = 0,
}

setClass(C, A)

function C:foo3()
  return string.format('Deaths(%d)', self.deaths)
end

D =
{
  __className = 'D',

  points = 0,
}

setClass(D, C)

function D:foo4()
  return string.format('Points(%d)', self.points)
end

function A:foo5()
  return 'Hello'
end

A.foo = 'Foo'

local a = A{ x = 7 }
print(a.x) -- 输出 7
print(a.y) -- 输出 0
print(a.kills) -- 输出 nil
print(a.deaths) -- 输出 nil
print(a.points) -- 输出 nil
print(a:foo1()) -- 输出 Position(7, 0)

local b = B{ x = 8, y = 1 }
print(b.x) -- 输出 8
print(b.y) -- 输出 1
print(b.kills) -- 输出 0
print(b.deaths) -- 输出 nil
print(b.points) -- 输出 nil
print(b:foo1()) -- 输出 Position(8, 1)
print(b:foo2()) -- 输出 Kills(0)

local c = C{ x = 9, y = 2 }
print(c.x) -- 输出 9
print(c.y) -- 输出 2
print(c.kills) -- 输出 nil
print(c.deaths) -- 输出 0
print(c.points) -- 输出 nil
print(c:foo1()) -- 输出 Position(9, 2)
print(c:foo3()) -- 输出 Deaths(0)

local d = D{ x = 10, y = 3 }
print(d.x) -- 输出 10
print(d.y) -- 输出 3
print(d.kills) -- 输出 nil
print(d.deaths) -- 输出 0
print(d.points) -- 输出 0
print(d:foo1()) -- 输出 Position(10, 3)
print(d:foo4()) -- 输出 Points(0)

print(a:foo5()) -- 输出 Hello
print(b:foo5()) -- 输出 Hello
print(c:foo5()) -- 输出 Hello
print(d:foo5()) -- 输出 Hello
print(a.foo) -- 输出 Foo
print(b.foo) -- 输出 Foo
print(c.foo) -- 输出 Foo
print(d.foo) -- 输出 Foo

print(a.isA) -- 输出 true
print(a.isB) -- 输出 nil
print(a.isC) -- 输出 nil
print(a.isD) -- 输出 nil

print(b.isA) -- 输出 true
print(b.isB) -- 输出 true
print(b.isC) -- 输出 nil
print(b.isD) -- 输出 nil

print(c.isA) -- 输出 true
print(c.isB) -- 输出 nil
print(c.isC) -- 输出 true
print(c.isD) -- 输出 nil

print(d.isA) -- 输出 true
print(d.isB) -- 输出 nil
print(d.isC) -- 输出 true
print(d.isD) -- 输出 true
2021-02-14 00:57:05