JassHelper Help

Interfaces

Polymorphism is an OOP concept in which different object classes may have the same action, although the action is different, the action gets the same name. For example both an ant and a person run, but they are pretty different objects and the run action is implemented in different ways.

An interface is like a set of rules struct types follow and allow you to call actions of an struct without really knowing the exact type of the struct.

interface printable method toString takes nothing returns string endinterface struct singleint extends printable integer v method toString takes nothing returns string return I2S(this.v) endmethod endstruct struct intpair extends printable integer a integer b method toString takes nothing returns string return "("+I2S(this.a)+","+I2S(this.b)+")" endmethod endstruct function printmany takes printable a, printable b, printable c returns nothing call BJDebugMsg( a.toString()+" - "+b.toString()+" - "+c.toString()) endfunction function test takes nothing returns nothing local singleint x=singleint.create() local singleint y=singleint.create() local intpair z=intpair.create() set x.v=56 set y.v=12 set z.a=45 set z.b=12 call printmany(x,y,z) endfunction

The printmany function takes three arguments of type printable, it does not know exactly which types the objects are, only that they are of struct types that extend printable

The rule for an struct to follow the printable interface is that it has a toString() method, the printmany function can use that method even though it does not know the exact types of the arguments

The toString() method is different for singleint and intpair, when printmany is called it calls the correct version of toString() for each argument, the result for the above sample is : 56 - 12 - (45,12).

Interfaces can have any number of methods and structs that extend them should implement all those methods, but can ther methods implemented as well.

It is illegal to declare onDestroy for an interface declaration, you can consider it to be automatically declared, you can use .destroy() on a variable of interface type and it will call the appropiate onDestroy method when necessary.

Interfaces can also implement variables, in this case interfaces allow some pseudo inheritance

interface withpos real x real y endinterface struct rectangle extends withpos real a real b static method from takes real x, real y, real a, real b returns rectangle local rectangle r=rectangle.create() set r.x=x set r.y=y set r.a=a set r.b=b return r endmethod endstruct struct circle extends withpos real radius=67.0 static method from takes real x, real y, real rad returns circle local circle r=circle.create() set r.x=x set r.y=y set r.radius=rad return r endmethod endstruct function distance takes withpos A, withpos B returns real local real dy=A.y-B.y local real dx=A.x-B.x return SquareRoot( dy*dy+dx*dx) endfunction function test takes nothing returns nothing local circle c= circle.from(12.0, 45.0 , 13.0) local rectangle r = rectangle.from ( 12.3 , 67.8, 12.0 , 10.0) call BJDebugMsg(R2S(distance(c,r))) endfunction

It is possible to acquire the type id of an instance of an struct that extends an interface, this type id is an integer number that is unique per struct type that extends that interface.

interface A integer x endinterface struct B extends A integer y endstruct struct C extends A integer y integer z endstruct function test takes A inst returns nothing if (inst.getType()==C.typeid) then // We know for sure inst is actually an instance of type C set C(inst).z=5 //notice the typecast operator endif if (inst.getType()==B.typeid) then call BJDebugMsg("It was of type B with value "+I2S( B(inst).y ) ) endif endfunction

In short, .getType() is a method that you use on instances of an object whose type extends an interface. And .typeid is an static constant set for struct types that extend an interface.

So, in the example we get to recognize that the given inst argument is of type C, then we can do the typecast and assignment.

There is another feature that uses typeids got another feature, and it is that interfaces got a constructor method that would create a new object given a correct typeid.

For example:

interface myinterface method msg takes nothing returns string endinterface struct mystructA extends myinterface method msg takes nothing returns string return "oranges" endmethod endstruct struct mystructB extends myinterface string x static method create takes nothing returns mystructB local mystructB m=mystructB.allocate() set m.x="apples" return m endmethod method msg takes nothing returns string return this.x endmethod endstruct struct mystructC extends myinterface string x //myinterface.create(...) can only use the default allocator or custom create //methods that take nothing. // //this declaration is not going to be taken into account if mystructC //is used in myinterface.create(...) // static method create takes string astring returns mystructC local mystructB m=mystructB.allocate() set m.x=astring return m endmethod method msg takes nothing returns string return this.x endmethod endstruct function test takes nothing returns nothing local integer T = mystructB.typeid local myinterface A set A=myinterface.create(mystructA.typeid) //this is not that useful since mystructA.create() does the same call BJDebugMsg(A.msg()) set A=myinterface.create(T) //this is more useful, we can create objects of variable types... call BJDebugMsg(B.msg()) set A=myinterface.create(122345) //using invalid values or 0 will make .create return 0 (no object) set A=myinterface.create(mystructC.typeid) //note that this will not use mystructC.create, just //mystructC.allocate, possibly causing errors, if this //happens you can an error message in-game if you compile under debug mode call BJDebugMsg(C.msg()) endfunction

If you plan using this feature, always be careful to handle 0 return values and also specify that it is better to use constructors without arguments on the interface's children.

It is also possible to declare the interface in a way that all the childs use a custom create method that returns nothing.

interface myinterface static method create takes nothing method qr takes unit u returns nothing endinterface struct st1 extends myinterface static method create takes nothing returns st1 //legal return st1.allocate() endmethod method qr takes unit u returns nothing call KillUnit(u) endmethod endstruct struct st2 extends myinterface integer k static method create takes integer f, integer k returns st2 //not legal local st2 s=st2.allocate() set st2.k=f+k*f return st2 endmethod method qr takes unit u returns nothing call ExplodeUnitBJ(u) endmethod endstruct

Interface methods allow you to use the defaults keyword. The defaults keyword allows methods to be optional when implementing the child struct. So if the method is not present in the child struct it will not show syntax errors requesting the method to be implemented. Instead we will implement a default empty method.

defaults is followed by nothing or by a value depending on the return type of the method (if the method returns nothing then it should default nothing, else it should default a value). defaults only supports constant values.

interface whattodo method onStrike takes real x, real y returns boolean defaults false method onBegin takes real x, real y returns nothing defaults nothing method onFinish takes nothing returns nothing endinterface struct A extends whattodo //don't forget the extends... method onFinish takes nothing returns nothing //must be implemented //.. code endmethod // We are allowed to add onBegin, but not forced to method onBegin takes real x, real y returns nothing //.. code endmethod // when somebody calls .onStrike on a whattodo of type A, it will return false endstruct struct B extends whattodo method onFinish takes nothing returns nothing //must be implemented //.. code endmethod // when somebody calls .onBegin on a whattodo of type A, it will do nothing endstruct
Last modified: 16 October 2024