Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Q&A

David Epstein, and

, April 01, 2000


Apr00: Java Q&A

What Is "JJ"?


Sidebar: Name That Programming Language

JJ is a programming language and environment designed for learning Java. Although a subset of Java, JJ includes advanced programming features, such as support for Design by Contract, complete with a class invariant, preconditions, and postconditions. We developed JJ after witnessing the rapid move to Java in introductory computer-science courses. It seemed that if an appropriate compiler existed with educational-based error messages, a subset of Java would more easily help programmers get started with Java. Since we also felt that the ideal educational language would not necessarily look like Java, we settled on a subset that uses a different syntax. After all, even though a C++/C-like syntax was selected for Java (primarily to attract experienced programmers), Java could just as easily have had a syntax similar to Pascal, Eiffel, or Basic. In a nutshell, JJ is based on Java, has some of Eiffel's features, and sports a syntax designed for ease of learning.

One reason behind this approach is that Java has features typically not taught to novice programmers. For example, JJ does not support inheritance, because most introductory programming courses do not get into inheritance. JJ does support classes as a mechanism for data encapsulation, but students should move from JJ to Java when they're ready for inheritance.

Another goal of JJ is to remove features that may lead to poor programming habits. For example, allowing modification of a public class data from another class is prohibited in JJ, as in Eiffel. Another way to describe this restriction is that public data can be seen, but not modified.

The JJ Language Definition

JJ consists of about three dozen commands, each beginning with a reserved word that must be the first word on a line. There is no need for a semicolon to end a command. For each construct, there is a unique End command. There is no need for curly brackets, or the dangling else problem (something found in C, C++, Java, and even Pascal). Mismatched constructs can be reported accurately, which can save beginning programmers hours of debugging.

It may be difficult for experienced programmers to remember how frequently beginners tend to misplace semicolons and curly braces. Even worse, it is truly impossible to predict the resulting error message. The error messages for missing or extra "{," "}," or ";" are confusing because compilers are built for experienced programmers and tend to report on "what was expected" instead of "what is likely wrong." For example, if you place an extra "}" at the end of a Java class, the javac compiler reports, "Class or interface declaration expected." Hopefully, Java beginners will pay attention to the line number and figure out that there is an extra ending "}." Unfortunately for beginners who actually read the error message, the word "interface" makes no sense, and the word "Class" in the error message might suggest changing the (previously correct) word "class" to "Class."

Should you make the edit from "c" to "C" in "Class," the next javac compile reports three errors, with the final one (and the one beginners tend to read first) suggesting that the name of the file should be Class.java. Java beginners who change the name of the file to Class.java may never find and fix the extra character -- the "}" that caused a major change.

Another problem with the overloaded "}" character in C, C++, and Java is the difficulty for compiler writers to accurately discover which "}" is missing. Is it the match for the "class," "function," "for," or the "if"? To report an accurate message using JJ, each construct has the unique End command: Class-EndClass, Routine-EndRoutine, If-EndIf, Repeat-EndRepeat, Constructor-EndConstructor, and Invariant-EndInvariant. Table 1 lists the JJ commands, along with their BNFs. This BNF snippet is, of course, incomplete without the rest of the production rules. The full JJ BNF is at http://jj.caltech.edu/doc/jjbnf.html.

If you have experience with multiple languages, you can probably grasp most of JJ from Table 1. Besides being visually apparent, each JJ command translates directly to one line of Java code. The full JJ language definition consists of the JJ BNF, used as a definition of the JJ syntax, and the JJ-to-Java translation definition, used as a definition of the JJ semantics. The JJ-to-Java translation definition is at http://jj.caltech.edu/doc/jj2java.html.

Initially, students typically focus on simple things such as variables and control structures. JJ's features include declarations (called Boxes), assignment (Set), Input and Output, conditionals (If, ElseIf, Else, EndIf), and loops (Repeat, ExitOn, EndRepeat). If there are no functions and routines, the commands are assumed to be in a "default" routine in a "default" class. Once students have these concepts under their belt, they typically focus on routines and functions. If functions and routines are defined without a class, they are assumed to be in a default class. Finally, new programmers write JJ code that contains classes, routines, and functions.

Example 1 presents three JJ versions of the classic "Hello, World" program. In fact, Example 1(a) might be the shortest "Hello, World" you have ever seen. Initially, the student does not have to see any classes, routines, or functions. Thus, JJ is both a procedural and a class-based language. Instructors can use this to their advantage by introducing only the concepts necessary and sufficient for each new step in learning to program.

Example 1(b) shows an optional middle stage that gives instructors the opportunity to introduce routines and functions without mentioning classes. If initial examples and labs necessitated the declaration of a single (useless) class, novice Java programmers might wonder about the class instruction's purpose. For many beginners, understanding routines and functions can be a large leap from simply listing commands. Not requiring all routines and functions to be in a class goes along with the goal of minimizing the number of times a Java beginner is told to just do something (put everything inside a class) without being given an explanation.

Example 1(c) is a JJ class. A JJ class could have class boxes (class variables/attributes), a class invariant that describes what is true about any object of the class when it is in a stable state, and a constructor to initialize these class boxes and satisfy the invariant. Listing One is a typical class -- a savings bank account. The invariant is optional, but the constructor is required since there is a public box named "identity."

The JJ Online Environment

From the outset, JJ was designed as an online learning environment for Java. That is, the JJ language is small enough that the JJ compiler runs in a browser. There is no need to download and install anything -- you simply get a JJ account, login, and begin programming. (JJ accounts are free for noncommercial educational use at http:// jj.caltech.edu/. Additionally, a guest account for DDJ readers to use at no charge is available at http://www.publicstaticvoidmain .com/ for a limited time. Go to "create account" and use the case-sensitive code of ddjguest.) Students enter JJ code in a panel and press the CheckIt button to compile. If there are no syntax errors, the equivalent line-by-line Java code is shown in an adjacent panel, side-by-side with the JJ code. Then, by pressing the CheckItOut button, the JJ (Java) program begins running as an applet in a new browser.

There are numerous benefits to an online strategy. First, there is no need for students to submit lab solutions as printouts or on diskettes. Instead, they press a button and submit the solution directly to the instructor via e-mail. Another benefit is that all compiled JJ code is stored on the server. JJ compiler developers and testers can review what students tried, the resulting error message, and what the student tried next (and when). JJ students can use a Back and Next button to traverse through their JJ code, including previous sessions (all the way back to their first JJ compile). Likewise, JJ instructors can use the Back button while helping students.

Additionally, JJ compiler error messages can be translated from English into native languages. All the errors have unique error codes that are used as tags in a web page, and instructors can set the URL of this web page. Thus, if instructors want to translate the error page, their students will be able to see errors in their native language.

Figure 1 shows the result of compiling a JJ Tic-Tac-Toe program, and Figure 2 shows the result of pressing the CheckItOut button and running this program. Listing Two is the Tic-Tac-Toe program. A larger version of this program that does not use arrays is at http:// jj.caltech.edu/examples/tictactoenoarrays.jj.

The Tic-Tac-Toe program highlights an advanced JJ feature -- the simplified version of Java's GUI definition (which is also available in Java). Another advanced feature available in the JJ language is support for Design by Contract (DbC), which is not directly supported in Java.

Design by Contract in JJ

How can Design by Contract be introduced in JJ for beginning programmers, and in such a way that is easily transferable to Java? Is it possible that, even though DbC is not always taught in college and appears to be an advanced feature only fully supported in Eiffel, DbC is quite natural if presented as a requirement of classes, routines, and functions?

Design by Contract, a phrase attributed to Bertrand Meyer (designer of Eiffel), is a programming methodology that includes three fundamental concepts (adopted from Hoare logic): preconditions, postconditions, and a class invariant. We derived a few simple language rules that cleared the way for an elegant JJ DbC definition.

Preconditions and Postconditions

A precondition is a requirement that a method (routine or function) has on the values supplied for its slots (parameters). This requirement is checked before the code of the method begins executing. A postcondition is a requirement that a method has on its result. This requirement is checked after the code of the method has completed. In JJ, preconditions are supported by the PreCheck command and postconditions are supported with PostCheck.

For example, assuming the routine "credit" expects a nonnegative amount, Example 2(a) shows how a PreCheck command is used to verify the value supplied for the slot named "amount." If the caller supplies a negative value, the author obviously did not understand (or check) the requirement. Essentially, the contract between the credit routine and the caller of the credit routine has been broken. If this happens, in DbC, "all bets are off." For JJ, "all bets are off" means (language rule #1) the error message is reported (along with the class name, method name, and line number) and the program halts.

The postcondition is slightly different. If a PostCheck bounces, the method did not produce the expected answer. The result is the same -- a contract is broken. In this case, the broken contract is localized to the author of the method. Recall that if a PreCheck bounces, some user of the class was not careful. This is an important distinction: A PostCheck should not bounce in a polished, published class, but it is likely that a PreCheck could bounce as a result of careless programming.

These concepts are valuable for beginners to grasp. They help teach new programmers the importance of precision for correctness and code reuse. JJ does not require the use of DbC, but instructors who like to emphasize the reusability of code (classes) can emphasize the responsibilities of specifying exactly what a method expects and is willing to return.

How are preconditions and postconditions implemented? In JJ, PreCheck and PostCheck commands are simply translated into if statements, and the bounce message is passed to a halt routine. For example, the PreCheck in Example 2(b) is translated to Java as Example 2(c).

If you are wondering about the value of DbC preconditions and postconditions compared to adding a few if statements, the idea is to accurately communicate a method's interaction with the caller. Unlike if statements, preconditions and postconditions are part of the public definition (interface) of a method. It is difficult for a tool to extract this public signature of a method if it has to automatically examine code to find the appropriate if statements. The preconditions and postconditions, however, are easy to locate and parse.

Another difference between pre- and postconditions and if statements is that the pre- and postconditions may or may not be compiled or executed at all. In other words, pre- and postconditions can be left on during testing, but turned off when code is deployed. This feature is usually a compile option. Typically, after testing, all postconditions are disabled. Leaving preconditions on is a good idea while integrating code, but should efficiency be an issue, they could be turned off after integration testing. Some would argue, on the other hand, that postconditions should also be left on.

Class Invariant

DbC is not complete without the contract for the class -- the class invariant. It is indeed valuable to have contracts on methods, but claiming support for DbC without a class invariant is like asking only two out of three passengers to fasten their seat belts.

Our definition of a JJ class invariant is "a set of rules that hold when an object is in a stable state." By "stable state" we mean (language rule #2) "immediately after the creation of the object (the call to the constructor) and when returning from any call from a public method (routine or function) of that object."

The class invariant is a list of requirements on the data in a class. Any instance of the class must meet these requirements except during a call to one of its public methods. A public method, while running, could change data in such a way that the invariant does not hold, but it has to restore the class invariant before completing.

The class invariant, like preconditions and postconditions, is part of a public description of a class. Users of a class benefit from knowing the requirements of the public entities of the class. For example, the author of a Person class at a phone company can communicate to the users of the class detailed information about a Person with a class invariant. If a Person consists of a firstName, lastName, and phoneNumber, the class invariant could, for example, specify one requirement -- every Person has a lastName (for example, the lastName is not null or an empty string).

At first, this class invariant may not appear valuable. But notice that the class invariant does not specify anything about the firstName attribute. The conclusion is that a Person must have a last name, but is not required to have a first name. The author of the Person class accepts "Pele," without requiring a first name. There is a similar conclusion with the phone number. Say that "Pele" has moved, and no longer has a phone number. Allowing a phone number to be null means that customers remain part of the program, possibly because they are expected to eventually get a new phone number.

Without a class invariant, this sort of information would have to be supplied in documentation. Documentation is notoriously hard to validate. A class invariant, however, is executable code and thus must also be kept up-to-date. As with the precondition and postcondition, the class invariant could be turned off should efficiency be an issue. Unlike preconditions and postconditions, which could be implemented with if statements, the class invariant does not have an easy implementation -- or does it?

Considering that the class invariant must hold after the constructor and after any public methods, it appears that simply calling the invariant as the last statement in the constructor and in any public methods obtains the goal. Unfortunately, the class invariant is a list of requirements, which means conditions, which means a potential for function calls. Might there be an infinite recursion problem should a condition in the class invariant reference a public function? Does this problem not also exist in the use of pre- and postconditions?

A second problem occurs when a public method modifies data, temporarily breaking the class invariant, with every intention of setting it back, and then references another method in the same class. Should the calling method have to reset the data to satisfy the class invariant before referencing another method? Does it matter if the other method is public or private? What happens if the method references a public method of another class that happens to (indirect) recursively call back?

Computer scientists have more than one answer to these questions, depending upon the context of the problem. It does seem unreasonable to require that a method satisfy the class invariant before referencing other methods, but this will break the simplistic implementation mentioned earlier, that of calling the class invariant at the end of every public method.

After giving you the key to supporting a class invariant in JJ, it may appear obvious and trivial.

  • Functions must be pure; that is, free of side effects (language rule #3).

  • Private methods cannot call public methods in the same class (language rule #4).

Pure Functions

Functions that are "free of side effects" are essentially functions that compute a result based on supplied values, if any. A function cannot change class data. Simple tests exist to determine if a function is free of side effects. For example, if the function foo() returns an int, the expression foo()+foo() must have the same effect as 2*foo(). A common phrase used to support this notion of a function is "Asking the questions should not change the answer."

One controversial issue with this rule is output. Some programmers use output statements for debugging, and this would break the aforementioned 2*foo() rule, since the output is part of the program's state. In JJ, a function cannot contain any Output and Outputln commands, but a function can contain Debug and Debugln commands that write to the Java console instead of to the output panel. The debug channel is, by definition, not part of the program's state and is thus writable.

How does requiring pure functions help support DbC in JJ? Recall that a problem for DbC language designers is handling a call to a function in the expression of a DbC precondition, postcondition, or class invariant. There are no recursion problems with pure functions. Thus, because functions cannot have side effects, there is no reason to verify the class invariant at the end of a public function.

Is it too restrictive to require functions to be free of side effects? That depends on the definition of side effects. For JJ, an educational language, modifying class data is considered a side effect. For Eiffel, modifying class data is allowed as long as the value is returned before the function completes. The Eiffel definition is, of course, extremely difficult for a compiler to check.

What about functions that return objects, something other than an int, real, bool, or Str? Creating the object requires a call to the constructor for that object. Calling a routine is not allowed in a function (since routines are supposed to have side effects). Is calling a constructor a side effect? Our conclusion is that calling a constructor is not a problem in JJ as (language rule #5) constructors are only allowed to initialize class data.

Privates Cannot Call Publics

Finally, (language rule #6) a private method of a class is not allowed to call a public method in that same class. Because of this rule, a public method does not have to satisfy the class invariant until it ends. This nicely solves another recursion problem mentioned earlier for DbC language designers: Public methods can call private methods without concern that there is an indirect recursive call back.

There are pedagogical reasons for this rule regardless of DbC considerations. A student first learning about methods may simply consider them as "common blocks of code." The influence of this rule hopefully puts the focus on determining the primary features of a class and writing the appropriate public methods. Should these public methods require utilities that do not belong as features of the class, these utilities become private methods.

Acknowledgment

Thanks to Bertrand Meyer for Eiffel, for Object-Oriented Software Construction, Second Edition (Prentice Hall, 1998), and for a brief review of JJ's DbC definition.

DDJ

Listing One

Import JJIO
Class Account
-- Name Ann Onymous
-- Does provide a simple savings bank account
-- Shows much of the programming language JJ
-- Shows PbC, Programming by Contract
-- Data attributes, fields
   Box balance ofType real is private 
   Box identity ofClass Str is public 

   Invariant
      Check (balance >= 0.00) 
         bounce "Negative balance!"
      Check (identity != null) bounce "Null id!"
   EndInvariant

   Constructor Account (b, i) is public
      Slot b ofType real -- account balance
      Slot i ofClass Str -- identification
   -- Does initialize or open the account
      PreCheck (b >= 0.00) 
         bounce "Negative initial balance!"
      PreCheck (i != null) 
         bounce "Null initial identity!"
      Set balance = b 
      Set identity = i
   EndConstructor Account

   Routine setBal (amount) is public
      Slot amount ofType real
   -- Does set or reset the amount of balance
      PreCheck (amount >= 0.00) 
         bounce "Setting negative balance!"
      Set balance = amount
   EndRoutine setBal

   Function getBal (none) ofType real is public
      Box result ofType real
   -- Does return balance in account
      Set result = balance
   EndFunction getBal

   Routine credit (amount) is public
      Slot amount ofType real
   -- Does deposit an amount into account
      PreCheck (amount >= 0.00) 
         bounce "Negative deposit!"
      Set balance = balance + amount
   EndRoutine credit

   Routine debit (amount) is public
      Slot amount ofType real
   -- Does withdraw an amount from account
      PreCheck (amount >= 0.00) 
         bounce "Negative debit!"
      PreCheck (amount <= balance) 
         bounce "Insufficient balance!"
      Set balance = balance - amount
   EndRoutine debit

   Function canCover (amount) ofType bool is public
      Slot amount ofType real
      Box result ofType bool
   -- Does check if balance covers amount
      Set result = (balance >= amount)
   EndFunction canCover
   
   Routine compound (percent, duration) is public
      Slot percent ofType real -- percent rate
      Slot duration ofType int -- time duration 
      Box rate ofType real -- interest rate
   -- Does compound balance at a rate for a time
      PreCheck (duration >= 1) 
         bounce "Negative duration!"
      Set rate = percent / 100.0
      Repeat
      ExitOn (duration == 0)
         Set balance = balance + balance * rate 
         Dec duration by 1
      EndRepeat
   EndRoutine compound

   Routine getFrom (account, amount) is public
      Slot account ofClass Account
      Slot amount ofType real
   -- Does transfer from one account to this
      PreCheck (account.balance >= amount) 
         bounce "Insufficient balance!"
      Call account.debit with (amount)
      Call credit with (amount)
   EndRoutine getFrom

   Routine show (none) is public
   -- Does display the account
      Output "Account " + identity + " has balance "
      Outputln balance
   EndRoutine show

   Routine test (none) is private
      Box his ofClass Account
      Box her ofClass Account
      Box amount ofType real
   -- Does test Account class
   Start
      New his ofClass Account with (100.0, "Dad")
      New her ofClass Account with ( 0.0, "Mom")
      Call his.credit with (200.00)
      Call his.compound with (10.0, 8)
      Call his.show
      Output "Enter her deposit "
      Input amount
      Outputln amount -- echo
      Call her.setBal with (amount)
      If his.canCover (600.00) then
         Call her.getFrom with (his, 600.00)
      Else
         Outputln "No transfer possible"
      EndIf
      Call her.debit with (300.00)
      Output "Her balance is " 
      Outputln her.getBal()
   EndRoutine test

EndClass Account

Back to Article

Listing Two

Import JJGui
Class TicTacToe
--Name: ?? (replace '??' to match your login name)
 Box   buttons   ofClass JJButton[] is private
 Box   ta        ofClass JJTextArea is private
 Box   gp        ofClass JJGridPanel is private
 Box   bp        ofClass JJBorderPanel is private
 Box   whoseTurn ofClass Str is private

 Constructor TicTacToe(none) is public
  Box i ofType int
   --allocate an array and loop thru New buttons
   NewArray buttons ofClass JJButton[9]
   Set i = 0
   Repeat
      New buttons[i] ofClass JJButton with ("")
      Inc i by 1
   ExitOn (i == 9)
   EndRepeat
   New ta ofClass JJTextArea with
    ("X's turn: To Start, click a button")
   New gp ofClass JJGridPanel with (3,3)
   New bp ofClass JJBorderPanel
   Set whoseTurn = "X"
 EndConstructor TicTacToe

 Routine ActsAsMain(none) is public
  Box i ofType int
   Start -- tells JJ that this is the "main"
   --loop thru adding buttons to panel
   Set i = 0
   Repeat
      Call gp.add with (buttons[i])
      Inc i by 1
   ExitOn (i == 9)
   EndRepeat
   Call bp.add with ("Center", gp)
   Call bp.add with ("South", ta)
   -- The next call is needed exactly once.
   Call bp.jjShowAsMainPanel
 EndRoutine ActsAsMain

 -- when a button is pressed, jjHandleButtonPress is
 -- automatically called with the button that was pressed.
 Routine jjHandleButtonPress(b) is public
  Slot b ofClass JJButton
  Box ok ofType bool
  Box i ofType int
  Box buttonLabel ofClass Str
  Box bCurr ofClass JJButton
   Set ok = false
   --loop thru buttons until finding the pressed one
   Set i = 0
   Repeat
      Set bCurr = buttons[i]
      If (b == bCurr) then
         Set buttonLabel = bCurr.getLabel()
         If (buttonLabel.length() == 0) then
            Set ok = true
            Call bCurr.setLabel with (whoseTurn)
         Else
            Call ta.setText with
             ("*** ERROR *** Button already pressed")
         EndIf
      EndIf
      Inc i by 1
   ExitOn (i == 9)
   EndRepeat
   If (ok) then
      If whoseTurn.equals("X") then
         Set whoseTurn = "O"
         Call ta.setText with
          ("O's next: Please click a button")
      Else
         Set whoseTurn = "X"
         Call ta.setText with
          ("X's next: Please click a button")
      EndIf
   EndIf
 EndRoutine jjHandleButtonPress
EndClass TicTacToe




Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.