This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kouksi44's profile picture

Classic - pretty OOP in CC

Started by Kouksi44, 25 March 2016 - 08:24 PM
Kouksi44 #1
Posted 25 March 2016 - 09:24 PM
CLASSIC

What is it ?

Classic is an api that offers users the possibility to use some common features of many oop languages.
That basically means you can use classes that inherit from each other and implement interfaces.

I know there are quite a few pretty similar oop libraries out there, but I haven´t found many that try to implement classes in a way that both looks pretty and is convenient to use.
Therefore I gave it try and finally after a few attempts I came up with this api.

How to use it:

The library allows the usage of two things: Classes and Interfaces.

Let´s look at how you create a simple class:


dofile("classic")

class "Shape" {
  colour;
}

That´s it ! We´ve created a simple class called "Shape" that has a property called "colour".
Anyways we can´t yet create an instance of this class as we still lack a method that serves as a constructor:


class "Shape" {
  colour;
}

function Shape:init(colour)
  self.colour=colour
  print("I am a simple shape !")
end

Now that we´ve added a init function we can create an instance of our class by writing:


local testshape=Shape(colours.red)

Of course we can´t do much with such an abstract "Shape" Object so let´s create a class that is a bit more usefull !


class "Square" extends "Shape" {
  x;
  y;
  width;
  height;
}

function Square:init(x,y,width,height,colour)
  self.x=x
  self.y=y
  self.width=width
  self.height=height
  self.colour=colour
end

As you can see the "Square" class inherits all properties from Shape by extending the class.

The last feature , this library has to offer are interfaces.
Interfaces are a basically classes without an init function:



interface "ITalk" {
  talk=function(self,text)
	print(self.name.." says :"..text)
  end;
}

class "Human" implements "ITalk" {
  ...
}

Every "Person" can now use the function talk to say something !
Of course you can also inherit from a class and implement an interface at the same time :

class "Male" extends "Human" implements "ITalk" {
  ...
}

Some further details:
SpoilerBoth classes and objects offer a variety of built in methods to make your life a bit easier!

Class methods:


getField(field) -- looks for the given string in the class members and returns its value

getSuper() -- returns the superclass

subclassOf(super) -- returns wether the given class is the superclass of the current class or not

superOf(subclass) -- returns wether the current class is the superclass of the given class

hashValue() -- returns a string that works as a unique identifier for the class

Object methods:


getClass() -- returns the class from which the Object was built

hashValue() -- same as Class:hashValue() but for an object


ToDo:
Spoiler- interfaces that only define raw function headers for classes to implement


Download:
Pastebin:
 pastebin get MfsdVz6U classic 

Let me know if there are any bugs or errors with this library !
Edited on 12 September 2016 - 06:49 PM
DannySMc #2
Posted 27 June 2016 - 06:58 PM
I can't believe no one has commented, can I just say great work, this is a really nice to use library and I do use it a lot, is there a way you can make it so it's loadable as an API instead of using dofile?

My only query is I think you have the wrong concept of interfaces as I use them in PHP applications, and they are defined as below:
http://stackoverflow.com/questions/2866987/what-is-the-definition-of-interface-in-object-oriented-programming
SquidDev #3
Posted 27 June 2016 - 07:02 PM
My only query is I think you have the wrong concept of interfaces as I use them in PHP applications, and they are defined as below:
Generally interfaces with a default definition are known as traits. How does it handle multiple interfaces with the same method name?
DannySMc #4
Posted 27 June 2016 - 07:05 PM
My only query is I think you have the wrong concept of interfaces as I use them in PHP applications, and they are defined as below:
Generally interfaces with a default definition are known as traits. How does it handle multiple interfaces with the same method name?

Not quite got to that, just writing a basic proof of concept program, to see how powerful it is, and I guess, but in PHP they are used to define inputs and outputs that's all, well with the frameworks I use, so apologies as I am mainly basing it off PHP.
Kouksi44 #5
Posted 28 June 2016 - 12:16 AM
I can't believe no one has commented, can I just say great work, this is a really nice to use library and I do use it a lot, is there a way you can make it so it's loadable as an API instead of using dofile?

My only query is I think you have the wrong concept of interfaces as I use them in PHP applications, and they are defined as below:
http://stackoverflow...ted-programming

Glad you like it ! I didn't really have any spare time to code in the last months, but there will definitely be an update to this library in the near future.

Adding support for os.loadApi shouldn't be too hard, it was simply more convenient for me to use dofile() as I could test my library directly in my editor that way.

The next update will probably contain a few other changes aswell:
- members can be declared as private and public
- members can be declared as static and final
- objects can be serialized and rebuilt
jv110 #6
Posted 28 June 2016 - 04:13 AM
I gotta say, your "method chain" method is brilliant. I'd never have thought of that.
DannySMc #7
Posted 28 June 2016 - 10:27 AM
I can't believe no one has commented, can I just say great work, this is a really nice to use library and I do use it a lot, is there a way you can make it so it's loadable as an API instead of using dofile?

My only query is I think you have the wrong concept of interfaces as I use them in PHP applications, and they are defined as below:
http://stackoverflow...ted-programming

Glad you like it ! I didn't really have any spare time to code in the last months, but there will definitely be an update to this library in the near future.

Adding support for os.loadApi shouldn't be too hard, it was simply more convenient for me to use dofile() as I could test my library directly in my editor that way.

The next update will probably contain a few other changes aswell:
- members can be declared as private and public
- members can be declared as static and final
- objects can be serialized and rebuilt

That's fine, well I would say you need to keep updating it and make it loadable via API!! As I want to use this in my main API for my programs, as this would be super useful.

But honestly really nice implementation, very clean, definitely one of my favourite implementations, as some people over complicate it too much I guess? and make it very bulky, this is small and clean. It's nice :P/>
Kouksi44 #8
Posted 28 June 2016 - 04:36 PM
Small update:

As far as I have tested it, loading the library through os.loadApi should work now.

Additionally, I have added some new features :
  • Members can now be made private or public ( public does not really differ from the normal way of defining members )
  • Members can now be made final so that their initial value can't be modified
Examples

Modifying members:


class "Example" {
   exampleValue = private - final - "Hello World";
}

local obj = Example()


As you can see, modifiers are put in front of the value of the member. They can be chained by separating them with "-".

In this example the value of exampleValue couldn't be changed and not be accessed from outside the Example class.

The pastebin link is still the same so simply run the install command once again !

If you find any bugs report them here !
Edited on 28 June 2016 - 03:03 PM
DannySMc #9
Posted 28 June 2016 - 05:31 PM
Small update:

As far as I have tested it, loading the library through os.loadApi should work now.

Additionally, I have added some new features :
  • Members can now be made private or public ( public does not really differ from the normal way of defining members )
  • Members can now be made final so that their initial value can't be modified
Examples

Modifying members:


class "Example" {
   exampleValue = private - final - "Hello World";
}

local obj = Example()


As you can see, modifiers are put in front of the value of the member. They can be chained by separating them with "-".

In this example the value of exampleValue couldn't be changed and not be accessed from outside the Example class.

The pastebin link is still the same so simply run the install command once again !

If you find any bugs report them here !



Hmm nice update, I would suggest seeing if you can get it to work as:

public function foo()

for variables and functions :P/>
Kouksi44 #10
Posted 28 June 2016 - 06:18 PM
Hm I'm pretty sure that I won't be able to implement that kind of syntax.
 public function foo() 
I have no idea how to make public work in front of the function keyword without any metatable magic.

However I will try adding static members so that all instances of a class share that value !
DannySMc #11
Posted 29 June 2016 - 01:33 PM
Hm I'm pretty sure that I won't be able to implement that kind of syntax.
 public function foo() 
I have no idea how to make public work in front of the function keyword without any metatable magic.

However I will try adding static members so that all instances of a class share that value !

Awesome okay!! :D/>
Kouksi44 #12
Posted 29 June 2016 - 09:50 PM
Next update:

Members can now be defined as shared - those members will share their values across all instances of a class.

Example:

class "SharedTest" {
   test = public - shared - "Hello World";
}

local first = SharedTest()
local second = SharedTest()

first.test="World Hello"
print(second.test)

Even though we only set the member of first to "World Hello" printing the value of the member of second will result in the same string being printed.

Let me know about any bugs with this new update !
unnamedcoder #13
Posted 30 June 2016 - 10:39 PM
nice, gives a lot of functionality that vanilla lua doesn't offer, almost as flexible as java
michty #14
Posted 02 July 2016 - 03:23 AM
I must say, good work. I was looking for something like this for quite some time. I really missed OOP in LUA!
Kouksi44 #15
Posted 03 July 2016 - 06:54 PM
Next update:

This update introduces a (IMO) pretty cool feature !
Members can now be declared to have certain datatypes so e.g. trying to set a member that was declared to hold only tables to anything else than a table will result in an error.

Example:

class "TestClass" {
  member = boolean - true
}

local obj = TestClass()
obj.member = "Hello World"  -- This will error !

This member can only be set to a value that is also boolean OR a nil value !

The only thing you have to keep in mind is, that lua already uses the string and table indexes.
Therefore if you want to define a string or table member you have to write "String" or "Table".

Besides the common lua types ( function, string, table, boolean, number and thread ) there are so called ClassTypes:
You can declare members to only hold objects of a certain class if the class has been defined previously.

Example:

class "Vector" {
  -- ...
}

class "TestClass" {
  member = Vector - Vector()
}

This update might have introduced a few bugs as it changed a lot of code in the background so let me know if anything seems to be broken !

Edit: Forgot to mention that ClassTypes basically check if the class of the given object equals the predefined ClassType or is atleast a subclass of that class!
Edited on 03 July 2016 - 05:25 PM
thecrimulo #16
Posted 05 July 2016 - 06:04 PM
I can use it as an API while I dofile() Classic? For example, making a shape class, and using it in other file, using dofile("classic") and dofile("myapi")
Kouksi44 #17
Posted 05 July 2016 - 06:31 PM
If you want to use a class that is defined in another file you can import that class by calling:
 using "path.to.the.class" 

Note that "." is used instead of "/" in the path
Lupus590 #18
Posted 05 July 2016 - 08:12 PM
If you want to use a class that is defined in another file you can import that class by calling:
 using "path.to.the.class" 

Note that "." is used instead of "/" in the path

any way to allow "/" instead of "." ?
why do you have to use "." ?
Exerro #19
Posted 05 July 2016 - 09:07 PM
-snip-

any way to allow "/" instead of "." ?
why do you have to use "." ?

Lua's require() uses ".", and I personally prefer it as it's more namespacey and less path/to/file-ey.

On the topic of the library:

This is awesome! It's really similar to how I did classes in Sheets, so naturally I'd think it has nice syntax and functionality. However, this has the added benefit of the magic metatable stuff for giving properties to fields of the class, which I have to admit is really clever and looks almost native.

My one real reservation is the name 'init' for the constructor. I've wanted to use a function called 'init' to initialise various things of a class in the past, so I think it'd be much better to use the name of the class as a constructor (i.e. Button:Button() not Button:init()).
Kouksi44 #20
Posted 05 July 2016 - 10:17 PM
-snip-

any way to allow "/" instead of "." ?
why do you have to use "." ?

Lua's require() uses ".", and I personally prefer it as it's more namespacey and less path/to/file-ey.

On the topic of the library:

This is awesome! It's really similar to how I did classes in Sheets, so naturally I'd think it has nice syntax and functionality. However, this has the added benefit of the magic metatable stuff for giving properties to fields of the class, which I have to admit is really clever and looks almost native.

My one real reservation is the name 'init' for the constructor. I've wanted to use a function called 'init' to initialise various things of a class in the past, so I think it'd be much better to use the name of the class as a constructor (i.e. Button:Button() not Button:init()).

Glad you like it!

A previous version of classic used the classname for the constructor but I think i changed it because I thought it looked a bit weird to write a function called
 function Button:Button () 

However I might change this again in the next update.

One thing I just realized :

Right now, you should avoid using ClassTypes or declaring class members that hold Objects in general.
Accessing those members won't work as copying those Objects once the class instance is created is a bit buggy.
Edited on 05 July 2016 - 08:13 PM
thecrimulo #21
Posted 06 July 2016 - 01:43 AM
-snip-

any way to allow "/" instead of "." ?
why do you have to use "." ?

Lua's require() uses ".", and I personally prefer it as it's more namespacey and less path/to/file-ey.

On the topic of the library:

This is awesome! It's really similar to how I did classes in Sheets, so naturally I'd think it has nice syntax and functionality. However, this has the added benefit of the magic metatable stuff for giving properties to fields of the class, which I have to admit is really clever and looks almost native.

My one real reservation is the name 'init' for the constructor. I've wanted to use a function called 'init' to initialise various things of a class in the past, so I think it'd be much better to use the name of the class as a constructor (i.e. Button:Button() not Button:init()).

Glad you like it!

A previous version of classic used the classname for the constructor but I think i changed it because I thought it looked a bit weird to write a function called
 function Button:Button () 

However I might change this again in the next update.

One thing I just realized :

Right now, you should avoid using ClassTypes or declaring class members that hold Objects in general.
Accessing those members won't work as copying those Objects once the class instance is created is a bit buggy.

So I can't pass an object as argument? I have something like this:

...
class "Window" implements "IWindow" {
  prop1;
  prop2;
  noObjectHereReally;
}
function Window:init(prop1,prop2)
  self.prop1 = prop1
  self.prop2 = prop2
end
interface "IDecorator" {
  draw = function(self, windowObject) -- Its only required here
	-- Code using values from windowObject
  end;
}
class "Decorator" implements "IDecorator" {
  prop1;
  prop2;
  noObjectHereReally;
}
function Decorator:init(prop1,prop2)
  self.prop1 = prop1
  self.prop2 = prop2
end
It ruins a bit my work :/
Edited on 05 July 2016 - 11:43 PM
Kouksi44 #22
Posted 06 July 2016 - 02:46 AM
-snip-

any way to allow "/" instead of "." ?
why do you have to use "." ?

Lua's require() uses ".", and I personally prefer it as it's more namespacey and less path/to/file-ey.

On the topic of the library:

This is awesome! It's really similar to how I did classes in Sheets, so naturally I'd think it has nice syntax and functionality. However, this has the added benefit of the magic metatable stuff for giving properties to fields of the class, which I have to admit is really clever and looks almost native.

My one real reservation is the name 'init' for the constructor. I've wanted to use a function called 'init' to initialise various things of a class in the past, so I think it'd be much better to use the name of the class as a constructor (i.e. Button:Button() not Button:init()).

Glad you like it!

A previous version of classic used the classname for the constructor but I think i changed it because I thought it looked a bit weird to write a function called
 function Button:Button () 

However I might change this again in the next update.

One thing I just realized :

Right now, you should avoid using ClassTypes or declaring class members that hold Objects in general.
Accessing those members won't work as copying those Objects once the class instance is created is a bit buggy.

So I can't pass an object as argument? I have something like this:

...
class "Window" implements "IWindow" {
  prop1;
  prop2;
  noObjectHereReally;
}
function Window:init(prop1,prop2)
  self.prop1 = prop1
  self.prop2 = prop2
end
interface "IDecorator" {
  draw = function(self, windowObject) -- Its only required here
	-- Code using values from windowObject
  end;
}
class "Decorator" implements "IDecorator" {
  prop1;
  prop2;
  noObjectHereReally;
}
function Decorator:init(prop1,prop2)
  self.prop1 = prop1
  self.prop2 = prop2
end
It ruins a bit my work :/

Passing Objects as arguments works perfectly fine.
Trying to do the following would not work :

class "Test" {
  member = SomeClass () -- can't be accessed by an object
}
local obj = Test ()
obj.member:someFunction ()
"member" holds an instance of "SomeClass" during the creation of the instance. Every object gets its own copy of the classtable but adding already defined objects to an instance is buggy without using the same object for all instances.

This will be fixed in the next update.
jv110 #23
Posted 11 July 2016 - 06:06 AM
IMO

class "uh" {
  l = "sh";

  f = function(self)
    self.l = "f"
  end;

  git = function(self)
    self.l = "git"
  end;
}
looks a lot better than

class "uh" {
  l = "sh";
}

function uh:f()
self.l = "f"
end

function uh:git()
self.l = "git"
end
thecrimulo #24
Posted 11 July 2016 - 01:03 PM
IMO

class "uh" {
  l = "sh";

  f = function(self)
	self.l = "f"
  end;

  git = function(self)
	self.l = "git"
  end;
}
looks a lot better than

class "uh" {
  l = "sh";
}

function uh:f()
self.l = "f"
end

function uh:git()
self.l = "git"
end
Actually I prefer the second one more than the first
Kouksi44 #25
Posted 12 July 2016 - 04:12 PM
IMO

class "uh" {
  l = "sh";

  f = function(self)
	self.l = "f"
  end;

  git = function(self)
	self.l = "git"
  end;
}
looks a lot better than

class "uh" {
  l = "sh";
}

function uh:f()
self.l = "f"
end

function uh:git()
self.l = "git"
end

It doesn't matter wether you define functions directly in the class table or add them later on, both ways do work with the library.

I pushed an update to pastebin that should solve the problem with members holding objects of a class.

Using :


class "Test" {
  t = SomeClass();
}

does work now. Every instance of the class "Test" will hold an exact clone of any objects in the classtable.

Edit:
I just noticed that I left some helper functions uncovered in the mainpost.
E.g. if you ever need to check wether a table is an instance you can use the
 typeof(obj) 
function. That function will return "instance" if the passed table is an instance of a class. If it's not then it will simply return the type of the passed argument.
Another thing that was added recently is the
 someObject.invokeMethod(sMethod,...)
method (Must be called with a dot). It allows access to the internal Object functions that the library makes use of to manually set/get values and get member properties / types. I don't know if that function will be useful that often, but if the the neccessity to call internal Object functions should ever arise - here you go!
Just keep in mind that these internal functions are normally called from within the library and are able to break an object if they are used in the wrong manner.

Methods you can access through invokeMethod() :
Spoiler
 Object:getValue(index,accessLevel)
That function is called whenever someone tries to access a value of the object. If for whatever reason you want to call this function, pass the index as the first argument and the accesslevel as the second one. ( More on accesslevels below )
 Object:setValue(index,value,accessLevel)
That function is called whenever someone tries to set a value of the object.
 Object:clone()
Returns a new instance of the objects' class that holds the exact same members as the original object.
 Object:checkMemberAccess(index,accessType)
Checks wether you can access the given index when setting/getting or not. ( If not, the function will error )
 Object:isTypedValue(index)
Returns wether the field at the given index has a fixed type or not.
 Object:isModifiedMember(index,_type)
Returns wether the field at the given index has the given meodifier (private,public,shared,final)
 Object:prepareMembers() 
Please do me a favor and never ever call this function. Totally broken objects guaranteed. ( Or atleast your object won't hold the same values as before)

Accesslevels:
It is important to pass a value for this argument. This number determines wether you are allowed to access a certain field or not. E.g. accessing private members requires an accesslevel of 0.
E.g. If you want to get a private value of a superclass, that would normally error. You can avoid that by calling
 obj.super.invokeMethod("getValue",privateField,0)
Edited on 12 July 2016 - 02:55 PM
Kouksi44 #26
Posted 14 July 2016 - 02:52 PM
Bug Fix and some changes :

Trying to get a private member will no longer error and simply return nil. This is because of a change in the inheriting system.

Currently if you had a class structure like that,
Spoiler
  • SuperClass
    • publicMember = "Hello World"
  • SubClass extends SuperClass
(poor formatting I know)

setting the publicMember through an object of SubClass resulted in a new field in SubClass that held the new value, instead of the value of the super class being set to the new value.
After this update an object will always check if the index it is setting to a new value is present in the super object and if so, will try to set that one instead ( that means if the member of the super object is public and not final )
Kouksi44 #27
Posted 16 July 2016 - 05:08 PM
New update:

Finally added properties with getters and setters to classic !

They can be declared exactly like any other modifiers are added to members:

class "Test" {
  prop = property - "Hello World";
}

E.g. trying to do the following:

local obj = Test()
print(obj.prop)

Will call the getter associated with that property:

function Test:getProp()
  return self.prop.."!"
end

If you find any bugs or have an idea what to add next just tell me !
Kouksi44 #28
Posted 10 September 2016 - 07:18 PM
Small update: You can now choose to use
 SomeObject:someFunction(...) 
or
 SomeObject.someFunction(...) 
when calling methods of an object. The library will automatically supply the correct self reference of the object to the method.

Edit: Found another bug, pastebin has been updated again.
Edited on 10 September 2016 - 06:10 PM
Adn #29
Posted 11 September 2016 - 07:46 PM
I plan to develop an Apt-like package management for CC (https://en.wikipedia.org/wiki/Advanced_Packaging_Tool). My goal is to create an application for easy installation of popular CC/Lua scripts without the need to do research for the pastebins. I'm currently collecting useful and popular scripts and libraries and your idea of this OOP library is very nice. That's why I plan to add it as "libclassic" for other scripts. It would be nice if you tell me your opinion about my idea and if you don't want to see classic in my repository.
Lyqyd #30
Posted 11 September 2016 - 08:46 PM
You might check out packman, it may be what you're looking to create.
Kouksi44 #31
Posted 11 September 2016 - 10:10 PM
I plan to develop an Apt-like package management for CC (https://en.wikipedia..._Packaging_Tool). My goal is to create an application for easy installation of popular CC/Lua scripts without the need to do research for the pastebins. I'm currently collecting useful and popular scripts and libraries and your idea of this OOP library is very nice. That's why I plan to add it as "libclassic" for other scripts. It would be nice if you tell me your opinion about my idea and if you don't want to see classic in my repository.

While I would recommend you to use existing options like Packman, you`re definitely free to use classic in any way you want to.
Kouksi44 #32
Posted 12 September 2016 - 08:48 PM
Bug fix once again: Passing the self reference as an argument for another instance method could`ve led to some problems with private members being accessible from outside the object. Should be fixed now.

Pastebin updated !
Adn #33
Posted 14 September 2016 - 05:36 PM
You might check out packman, it may be what you're looking to create.

Ah, I didn't know there was such a solution.
Kouksi44 #34
Posted 30 January 2017 - 10:30 PM
New update:

I rewrote this library from the ground up, so this update comes with quite a lot of changes.

Classic 2.0

First of all defining classes that extend other classes changed a little bit:


class "Shape" {
  color = private - colours.red;
}

class "Rectangle" : Shape {
  width = private - 0;
  height = private - 0;
}

Instead of using the extends keyword you just use a colon now.

Also, instead of using

function SomeClass:init() end
You now use the name of the class as the constructor name:

function SomeClass:SomeClass() end

Accessing values of a class is now possible by simply indexing it:


class "Test" {
  x = static - 234;
}

print(Test.x) -- prints 234

Another new feature is, that you can now define your class to to be at a specific classpath:

class "myProject.subFolder.TestClass" {
  ...
}

If you want to create an instance of such a class you either have to import that namespace or specific class:

using "myProject.subFolder.TestClass"
or

using "myProject.subFolder.*" -- imports all classes in this namespace

Or you use the full path to the class:

local obj = myProject.subFolder.TestClass()

Note: You first have to load a class from whatever file it is located in and after that make it actually usable by calling using.

You also cannot extend from classes that are not in the default global namespace and are not imported yet:

class "SomeClass" : myProject.subFolder.TestClass {} -- does not work

While in previous version of Classic, you could set the type of a member, this is no longer possible in this version. I really felt like it was a bit unnecessary and and too slow overall.
You can still make members : public, private, final and static

That's it for now ! Let me know if there are any bugs in this version !

Updated pastebin link:

pastebin get gjTfwQ8P
Edited on 30 January 2017 - 09:48 PM
Mao Zedong #35
Posted 31 January 2017 - 05:18 PM
OOP is a terrible idea. We need more λ in ComputerCraft.
Kouksi44 #36
Posted 31 January 2017 - 08:27 PM
What exaclty makes oop such a terrible idea for you ? Lua does contain lambda calculus if that is what you mean…
Nothy #37
Posted 02 March 2017 - 09:09 AM
I need to check this out. I've read through the entire thread and this looks super promising.
Kouksi44 #38
Posted 11 March 2017 - 05:28 PM
New update:

Because of whatever reasons, I decided to rewrite this library once again… Classes and objects do no longer use any metatable magic to make inheritance possible. So theoratically the new version should perform a tiny bit better, even though it still won't be the fastest.

Apart from what changed under the hood, most things are still realtively the same, except for a few small changes and new features:

Creating and extending classes now works again how it used to before the last update:
 class "mypackage.blah.Test" extends "myotherpackage.bluh.ImportantClass" { ... } 

That way it is much easier to inherit classes that are not in the default namespace.

Also, method overriding does actually work how it should work now:

class "A" {

y = public - function()  ...  end;

z = public - function(self)  self.y()  end

}

class "B" extends "A" {

test = function(self)  self.z() end

y = public - function()  print("This is called by A.z") end
}

A method can only be overriden, if it is public and neither static nor final.
I also added the baseclass "CObject" that is the baseclass of all classes that don't already inherit from some class. CObject offers a view useful methods:
Spoiler

hashValue() -- returns a unique value for the object this method was called by
getClass() -- returns the class of the object this methods was called by
getClassName() -- returns the runtime class name of the object
instanceof(sClassName) -- returns wether the object is an instance of the class with the given name. if the class has a package name, that name must be part of the argument

The last change is how you import classes from another file. That part is much easier now:
 using "mypackage.blah.TestClass" : from "TestClass.lua" 

If the class you want to import is already loaded, the function will simply put it in the default namespace, so that the package name can be omitted. In that case it's not necessary to call the :from() function.

Pastebin installer:
 pastebin run fq9fwb2S 
Edited on 11 March 2017 - 04:29 PM
Kouksi44 #39
Posted 12 April 2017 - 02:37 PM
Another update:
This one adds several new features that should be quite useful.
First of all method and field resolving should work a bit better now. Also most error messages will now print the correct class in which the method or field lookup failed.

Anonymous classes:
You can now easily define an anonymous class that extends some existing class pretty much the same way as you would do in Java :

class "SomeClass" {
  bar = public - function() end;
}

local anonymous = SomeClass() {
  bar = public - function()
	-- this overrides SomeClass.bar
  end;
}
Note that these anonymous classes are treated as objects of the class they are extending. Any new methods that are added in the anonymous class that do not exist in the superclass won't be visible

Generic classes :
You can now specify a type parameter in the head of the class definition like so:

class "SomeClass" : T {
  someValue = public - T - nil;
}
-- also works with inheritance

class "SomeClass" extends "SomeOtherClass" : T {
  ...
}

local obj = SomeClass[String](...)

Properties:
The property keyword should be pretty straightforward. Instead of directly setting and getting some value, the respective getter and setter methods are called when a property is accessed.

class "SomeClass" {
  x = public - property - String - "Hello World";
  getX = function(self) ... end;
  setX = function(self,value) end;
}

Some other changes:
- Attributes can be given certain types again, just specify the type like any other keyword: String, Table, boolean, thread, number.
- The type of an attribute can also be an existing class. Any object that is assigned to such an attribute is treated as an object of that class. That means, that the object must be castable to the given type.
- Calling getClasss() on an object will now return an instance of the built in Class class. That class offers some helper functions:
Spoiler

getDeclaredFields()
getDeclaredField(fieldName)
getDeclaredMethods()
getDeclaredMethod(methodName)
getName()
getPackage()
getSuperclass()
toString()
hasTypeParameters()

The installer is still the same:

pastebin run fq9fwb2S