Interface Classes
An interface in UnrealScript is a lot like a class except that it does not define the functionality that it declares. This is best illustrated with an example of an interface named Breakable:
Interface Breakable;
function float Add(float A, float B);
function PrintString(string S);
Notice a couple of things:
- The keyword used to define an Interface is Interface
- The functions defined have no bodies
An interface in UnrealScript works a lot like how an interface works in Java. It is useful for classifying objects into categories. In order to make use of an interface, a class must implement it. This is done via the implements keyword. For example, here's an Actor subclass SampleActor that implements the above interface.
class SampleActor extends Actor implements(Breakable);
function float Add(float A, float B)
{
return A + B;
}
function PrintString(string S)
{
`Log(S);
}
Defaultproperties
{
}
The implements keyword indicates that this Actor will fulfil the contract set out by the Breakable interface, that's to say that it provides implementations for all of the functions defined in all of the interfaces that it implements. In this case, the Actor implements only a single interface, Breakable, so it needs to provide implementations for its functions, as seen above.
Multiple Interface Implementation
It is possible for a class to implement multiple interfaces. This is done by declaring all of the interfaces that the actor implements in a comma-separated list in the parentheses beside the implements keyword. For example, given the following additional interface definition:
Interface Malleable;
function Bend(float angle);
We can redefine our SampleActor above as follows:
class SampleActor extends Actor implements(Breakable, Malleable);
function float Add(float A, float B)
{
return A + B;
}
function PrintString(string S)
{
`Log(S);
}
function Bend(float angle)
{
`Log("I now bend by "$angle);
}
Defaultproperties
{
}
In this way, it is possible for a class to implement any number of interfaces. It is required that a class that implements an interface must provide implementations for all of the functionality that the interface declares.
Interface Use Cases
There are two situations where interfaces are useful and they are typically inter-related.
Iterating Over Actors Implementing An Interface
This is one of the most common use cases for an interface as it can provide a way for us to organize our Actor types without using the strict rules of single parent inheritance. Let's say for example that you would like to iterate over all actors in the scene that implement the Malleable interface. You may write code like this:
// Assume that this is inside a function
local Actor iterActor;
foreach AllActors(class'Actor', iterActor, class'Malleable')
{
// Do stuff with iterActor ...
}
Note that we've passed a third argument to AllActors. The third argument is optional but we are using it in this case. It tells the iterator that we are only interested in Actors that implement the Malleable interface.
This is all fine and good but we cannot call any of the interface's functions on iterActor in its current state. This is because, as far as UnrealScript is concerned, iterActor is of type Actor and type Actor does not implement Malleable. But, we know that iterActor does implement Malleable because we've definitely requested this from AllActors! To solve this issue, we must cast.
Casting To An Interface
An interface is like a class and so we typically have to cast an object to it before we can use its functionality. For example, we can update the above example as follows:
// Assume that this is inside a function
local Actor iterActor;
local Malleable malleable;
foreach AllActors(class'Actor', iterActor, class'Malleable')
{
malleable = Malleable(iterActor);
// Always test to ensure that the cast succeeded.
// Although in this case it will surely succeed...
if (malleable != None)
{
malleable.Bend(62);
}
}
In order to use the functions declared in Malleable, we had to cast our Actor variable to a Malleable variable, just like you do with classes; the underlying object instance remains the same, the syntactic symbol we used to refer to it has simply changed.
Interface Inheritance
It is possible for Interfaces to extend each other but this is not recommended for a number of reasons.
- The way that UnrealScript is set up, if a class implements two interfaces that share a common ancestor, that class' virtual function table (the underlying mechanism by which virtual functions are achieved in UnrealScript) will be corrupt
- Conceptually, interfaces that extend each other are less useful. Normal class-style inheritance can be used to achieve this. The power of interfaces comes from their ability to cut horizontally through the inheritance tree, so it is not recommended that they in turn create their own inheritance hierarchy.