• Increase font size
  • Default font size
  • Decrease font size
Home Article Sections Objective-J Basics Introduction to Objective-J

Introduction to Objective-J

E-mail Print PDF
Article Index
Introduction to Objective-J
Separating Class Files
Inheritance
All Pages

This article is a "gentle" introduction to Objective-J. It is intended to show a few of the capabilities, not a complete introduction to the language. It should be enough to get you stared and comfortable with the language.

Objective-J is a programming language written by the guys at 280 North. It looks and feels very similar to Objective-C so if you're familiar with programming Cocoa in OS X you'll feel right at home. If not it's OK, you can pick it up rather quickly. Either way there are some differences and "gotchas" that we'll try to point out here.

We'll assume that you have a working installation of Cappuccino and the Objective-J tools for this tutorial. If you don't, please browse through the "setting up" tutorial here or the official getting started tutorial and get things installed. We won't be doing any Cappuccino or fancy GUIs here. Just plain Objective-J run through the command-line tool "objj".

First Steps

Let's begin our tour, like pretty much all language tutorials, by building a simple class and driver program(s). Since I have a rather mathematical bent, we'll build a very simple vector class. Here we just want to represent a 3D point in space with the (x,y,z) coordinates. We'll build on this simple class a little bit through this tutorial but we'll make it easy - all the source code is included in the "attachments" at the end of the tutorial. Each file has the not-terribly-creative name like "Point1.j" or "driver6.j". The numbers indicate where in this tutorial chain the source belongs - starting at 1 in a non-C fashion. So let's begin with the file "Point1.j".

Our vector class, called "Point" is defined with the following lines:

@import <Foundation/CPObject.j>
@implementation Point : CPObject 
{
   float x;
   float y;
   float z; 
} 

The first line includes (imports) the required Foundation library object "CPObject". We have to do that for all of our Objective-J classes since they all have to be derived from something. The next lines define our class - here called "Point" - as having three floating point variables x, y, and z. Notice this is a little different than Objective-C. Gone are the separate header files with the "@interface" and source files with the "@implementation" directives. Now we have a single file for both. Here we're deriving the Point class from the base Foundation class "CPObject". We have a class defined but we can't really do much with it since it has no functions defined. Let's add the necessary initialization function and some nice accessors.

- (id)init 
{
  self = [super init];
  x = y = z = 0.0;
  return self;
}
- (float)x   { return x; }  
- (float)y   { return y; }
- (float)z   { return z; }
@end

We've now created he initializer (called "init") as well as an accessor for each of the variables. Notice that Objective-J, like Objective-C, allows a method and a variable to have the exact same name. We now have a (very simple) working class. Let's add a little test driver to exercise the new class:

var p = [[Point alloc] init];
print("Point: x="+[p x] + "   y="+[p y] + "   z="+[p z]);

Two little lines, so much going on. Here's the low-down. First some syntax help. If you've never seen it before, the Objectve-J way of calling member functions is with the "[object message]" syntax. Conceptually we're sending the object the specified message, or, alternatively, we calling the "message" member function of the given object. The syntax looks a little strange if you're coming from a C++ or Java world, but the Objective-C and Smalltalk hackers will feel right at home. Anyway, play around a bit, you'll get used to it. Back to the code... In line one we're making successive calls (passing successive messages) to an object. The code "[Point alloc]" grabs the required memory for a Point object and returns the newly-created object. We then send the returned object (a Point) the "init" message, giving it no parameters. Remember we implemented the init method above. In that code we just zeroed out the x, y, and z member variables. The result of the init message is then stored in a variable called "p". One quick note: unlike C++ (or Objective-C) we don't need to specify the type of the variable p. The term "var" above is used for scoping. Any variable declared without the var keyword will become global. We usually want to keep them local so we'll use the var keyword. In this instance it doesn't really matter (it'll run identically without it) but it's good practice.

The second line above calls the print function which takes a string parameter and prints it on the console. Notice we're again passing messages to the p object, in this case the getter functions for x, y, and z. The getters return floating point data but in Objective-J these are automatically converted to a string by the spring append function "+". The plus operator is overloaded so for strings it appends while for numeric types it adds, as you would expect. So that one print line sends three messages to p asking for three pieces of data, converts the returned data to strings, forms the final long string by pushing together all the strings, and finally prints the resulting string to the console... Whew.

Give it a try. Copy the three code sections above into a text file called "Point1.j" (or grab and save the one from the attachments below) and run it through the objj interpreter:

 > objj Point1.j
Point: x=0 y=0 z=0

Well, there you have it. We've created a point object and printed it out. Hmmm... actually it's not a terribly good class because we don't have any nice way to change the values of of the coordinates (x,y, and z). In Objective-J we don't have access control on the member data so x, y, and z are public. That means we can change the data values directly like this:

p.x = 10;
p.y = 20;
p.z = 30;
print("Point: x="+[p x] + "   y="+[p y] + "   z="+[p z]);

As you'd expect, this will produce the output

 Point: x=10   y=20   z=30

Changing member data this way is not usually good form so in the next section we'll add a couple access functions.

Accessors

In the last section we left the code with dangling private data. That's not usually good form in polite society so we'll add some accessor functions. Specifically, add the following to the Point1.j code and save it as Point2.j (or grab it from the attachments).

- (id)initWithX: (float)newX andY: (float)newY andZ: (float)newZ
{
  self = [super init];
  x = newX;
  y = newY;
  z = newZ;
  return self;
}
- (void)setX: (float)newX
{
  x = newX;
}
- (void)setY: (float)newY
{
  y = newY;
}
- (void)setZ: (float)newZ
{
  z = newZ;
}
- (CPString)string
{
  return "Point:   x="+x + "   y="+y + "   z="+z;
}

Here we've added five new functions. Four of them are really very straightforward: the setX, setY, and setZ routines take a single parameter, and set the corresponding member data to this new value. This gives us a nice, clean way to change values rather than directly accessing them as public (data hiding and all). We've also added a method to convert the point to a string. We'll use this in conjunction with the print function to make things a little neater.

The last function (actually first in the listing) is a fancier initializer. Rather than initializing with "init" then having to set each value separately through set calls, we'll add a three argument initializer, formally called "initWithX:andY:andZ:". This is an Objective-C-ism (influenced by Smalltalk) which, while much more verbose, lets us identify the parameter names so we know what order they're supposed to be in. If you write your function names correctly, sending a message should read like a sentence. We can see that when we add the following driver code:

var p  = [[Point alloc] init];
var p2 = [[Point alloc] initWithX:0.0 andY:1.0 andZ:2.0];
var p3 = [[Point alloc] initWithX:2.0 andY:1.0 andZ:0.0];

print([p string]);
print([p2 string]);
print([p3 string]);

[p3 setX: -2.0];
[p3 setY: -1.0];
[p3 setZ:  0.0];
print([p3 string]);

The first two lines are the same old initialization and printing we did before. In lines 4 and 7 we use the new initializer to set up the variables p2 and p3. In lines 10 through 12 we use the new setters to change the value of p3. Running this gives

 >objj Point2.j
 Point:   x=0   y=0   z=0
 Point:   x=0   y=1   z=2
 Point:   x=2   y=1   z=0
 Point:   x=-2   y=-1   z=0

Danger! Danger!

Here's an interesting "gotcha". In our definition of the Point class in Point2.j we declared the member data like this (see Point2_bad.j):

@implementation Point : CPObject
{
  float x;
  float y;
  float z;
}

which gives the correct answer as shown earlier. If you change the declaration a touch to what is perfectly reasonable in, say Objective-C or C++, to this:

@implementation Point : CPObject
{
  float x, y, z;
}

bad things happen, man, bad things! With this declaration the results become the clearly wrong:

 > objj Point2_bad.j
 Point:   x=2   y=1   z=0
 Point:   x=2   y=1   z=2
 Point:   x=2   y=1   z=0
 Point:   x=-2   y=-1   z=0

It would appear that the x and y variables are global rather than members of the object so when p3 is created the (now global) values of all the x and y values are changed, corrupting the data for objects created earlier (p1 and p2). We can avoid this by just declaring the variables individually.

Automatic Accessors

When we build classes we almost always end up writing accessors (setters and getters) for the member data.  Luckily the good folks developing Objective-J have given us a semi-automatic way to make them.  This saves us a ton of typing.  Let's change the code that defines the class to the following:

@implementation Point : CPObject
{
  float x @accessors;
  float y @accessors;
  float z @accessors;
}

Here we've added the "@accessors" directive to the declaration of the member data (here x, y, and z).  This informs the compiler to generate the getter and setter functions automatically for us.  That is, it makes the x and setX routines (likewise for the y and z).  This saves us from having to type them, saving time for those of us who type rather slowly.  Check out the Point3.j file from the attachments to see the complete code.



Last Updated ( Tuesday, 10 November 2009 05:42 )  

Design by i-cons.ch / etosha-namibia.ch