The busy Java developer's guide to Scala: Packages and access modifiers

Scala's view of the public, the private, and all points in between

Code has to be referenced and packaged in the real world, and in this installment of his series, Ted Neward corrects an egregious oversight by covering Scala's package and access modifier facilities. Then, he continues the exploration of the functional side of Scala, this time having a quick look at the "apply" mechanism.

Share:

Ted Neward, Principal, Neward & Associates

Ted Neward photoTed Neward is the principal of Neward & Associates, where he consults, mentors, teaches, and presents on Java, .NET, XML Services, and other platforms. He resides near Seattle, Washington.



29 July 2008

Also available in Chinese Russian Japanese

Recently, reader feedback has led me to realize that I have left behind an important aspect of Scala's language while crafting this series: Scala's package and access modifier facilities. So I'm going to take a moment and cover this before diving into one of the more functional elements of the language, the apply mechanism.

Packaging

To help segregate code in such a way that it doesn't conflict with one another, Java™ code provides the package keyword, creating a lexical namespace in which classes are declared. In essence, putting a class Foo in a package named com.tedneward.util modifies the formal class name to com.tedneward.util.Foo; it must be referenced as such. Java programmers will be quick to point out that they don't do this, they import the package and thus save themselves from having to type the formal name out. This is true, but it merely means that the work of referencing the class by its formal name falls to the compiler and bytecode. A quick glance at javap output reveals this to be the case.

About this series

Ted Neward dives into the Scala programming language and takes you along with him. In this exciting developerWorks series, you'll learn what all the recent hype is about and see some of Scala's linguistic capabilities in action. Scala code and Java code will be shown side by side wherever comparison is relevant, but (as you'll discover) many things in Scala have no direct correlation to anything you've found in Java code -- and therein lies much of Scala's charm! After all, if Java code could do it, why bother learning Scala?

Packages in the Java language have a few quirks to them, however: The package declaration must appear at the top of the .java file in which the package-scoped classes appear (which causes some serious havoc with the language when trying to apply annotations to the package); the declaration holds scope across the entire file. This means that the rare case in which two classes are tightly-coupled across package boundaries has to be split across files, leading the unwary to not recognize the tight coupling between the two.

Scala takes a slightly different approach in respect to packaging, treating it as a combination of the Java language's declaration approach and C#'s scoped approach. With that in mind, a Java developer can do the traditional Java approach and put a package declaration at the top of a .scala file just as normal Java classes do; the package declaration applies across the entire file scope just as it does in Java code. Alternatively, a Scala developer can use Scala's package "scoping" approach in which curly braces delimit the scope of the package statement, as in Listing 1:

Listing 1. Packaging made simple
package com
{
  package tedneward
  {
    package scala
    {
      package demonstration
      {
        object App
        {
          def main(args : Array[String]) : Unit =
          {
            System.out.println("Howdy, from packaged code!")
            args.foreach((i) => System.out.println("Got " + i) )
          }
        }
      }
    }
  }
}

Effectively, this code declares one class, App, or to be more precise, a single class called com.tedneward.scala.demonstration.App. Note that Scala also permits the package names to be dot-separated, so Listing 1 could be written more tersely as shown in Listing 2:

Listing 2. Packaging made simple (redux)
package com.tedneward.scala.demonstration
{
  object App
  {
      def main(args : Array[String]) : Unit =
      {
        System.out.println("Howdy, from packaged code!")
        args.foreach((i) => System.out.println("Got " + i) )
      }
  }
}

Use whichever style seems more appropriate because they each compile into exactly the same code constructs. (The scalac compile goes ahead and generates the .class files in package-declared subdirectories as javac does.)

Import

Of course, the logical flip-side of packaging is import, Scala's mechanism for bringing names into the current lexical namespace. Readers of this series have already seen import in a couple of samples before now, but it's time for me to point out some of import's features that will come as a surprise to Java developers.

First of all, you can use import anywhere inside the client Scala file, not just at the top of the file and correspondingly, will have scoped relevance. Thus, in Listing 3, the java.math.BigInteger import is scoped entirely to the methods defined inside the object App and nowhere else. If another class or object inside of mathfun wanted to use java.math.BigInteger, it would need to import the class just as App did. Or if several classes in mathfun all wanted to use java.math.BigInteger, the import could occur at the package level outside the definition of App and all of the classes in this package scope will have BigInteger imported.

Listing 3. Import scoping
package com
{
  package tedneward
  {
    package scala
    {
        // ...
      
      package mathfun
      {
        object App
        {
          import java.math.BigInteger
        
          def factorial(arg : BigInteger) : BigInteger =
          {
            if (arg == BigInteger.ZERO) BigInteger.ONE
            else arg multiply (factorial (arg subtract BigInteger.ONE))
          }
        
          def main(args : Array[String]) : Unit =
          {
            if (args.length > 0)
              System.out.println("factorial " + args(0) +
                " = " + factorial(new BigInteger(args(0))))
            else
              System.out.println("factorial 0 = 1")
          }
        }
      }
    }
  }
}

Importing doesn't stop there, however. Scala sees no real reason to differentiate between top-level members and nested ones, so you can use import to bring not just nested types into lexical scope, but any member; by importing all of the names inside java.math.BigInteger, for example, you can drop the scoped references to ZERO and ONE to just name references as in Listing 4:

Listing 4. Static imports ... without the static
package com
{
  package tedneward
  {
    package scala
    {
        // ...
	
      package mathfun
      {
        object App
        {
          import java.math.BigInteger
          import BigInteger._
        
          def factorial(arg : BigInteger) : BigInteger =
          {
            if (arg == ZERO) ONE
            else arg multiply (factorial (arg subtract ONE))
          }
        
          def main(args : Array[String]) : Unit =
          {
            if (args.length > 0)
              System.out.println("factorial " + args(0) +
                " = " + factorial(new BigInteger(args(0))))
            else
              System.out.println("factorial 0 = 1")
          }
        }
      }
    }
  }
}

By using the underscore (remember the wildcard character in Scala?), you effectively tell the Scala compiler that all of the members inside BigInteger should be brought into scope. And because BigInteger has already been put into scope by the previous import statement, there's no need to explicitly package-qualify the class name. In fact, these could even be combined into a single statement because import can take multiple, comma-separated targets to import (shown in Listing 5):

Listing 5. Bulk imports
package com
{
  package tedneward
  {
    package scala
    {
        // ...
	
      package mathfun
      {
        object App
        {
          import java.math.BigInteger, BigInteger._
        
          def factorial(arg : BigInteger) : BigInteger =
          {
            if (arg == ZERO) ONE
            else arg multiply (factorial (arg subtract ONE))
          }
        
          def main(args : Array[String]) : Unit =
          {
            if (args.length > 0)
              System.out.println("factorial " + args(0) +
                " = " + factorial(new BigInteger(args(0))))
            else
              System.out.println("factorial 0 = 1")
          }
        }
      }
    }
  }
}

This saves you a line or two. Note that the two cannot be combined: the first imports the BigInteger class itself and the second, the various members inside that first class.

You can also use import to introduce other non-constant members as well. For example, consider a math utility library (of questionable value perhaps, but still...) in Listing 6:

Listing 6. Enron's accounting code
package com
{
  package tedneward
  {
    package scala
    {
        // ...
      
      package mathfun
      {
        object BizarroMath
        {
          def bizplus(a : Int, b : Int) = { a - b }
          def bizminus(a : Int, b : Int) = { a + b }
          def bizmultiply(a : Int, b : Int) = { a / b }
          def bizdivide(a : Int, b : Int) = { a * b }
        }
      }
    }
  }
}

Using this library could get quite annoying over time, having to type BizarroMath every time one of its members was requested, but Scala allows for each of the members of BizarroMath to be imported into the top-level lexical namespace, almost as if they were global functions (shown in Listing 7):

Listing 7. Calculating Enron's expenses
package com
{
  package tedneward
  {
    package scala
    {
      package demonstration
      {
        object App2
        {
          def main(args : Array[String]) : Unit =
          {
            import com.tedneward.scala.mathfun.BizarroMath._
            
            System.out.println("2 + 2 = " + bizplus(2,2))
          }
        }
      }
    }
  }
}

There are other interesting constructs that would allow a Scala developer to write the more natural 2 bizplus 2, but that will have to wait for another day. (Readers curious about a potentially heavily-abusable Scala feature can look at the Scala implicit construct as covered in Programming in Scala by Odersky, Spoon, and Venners.)

Access

While packaging (and importing) are part of the encapsulation and packaging story in Scala, a large part of it, as with Java code, lies in its ability to restrict access to certain members in a selective way — in other words, in Scala's ability to mark certain members "public," "private," or somewhere in-between.

The Java language has four levels of access: public, private, protected, and package-level access (frustratingly applied by leaving out any keyword). Scala:

  • Does away with package-level qualification (in a way)
  • Uses "public" by default
  • Specifies "private" to mean "accessible only to this scope"

By contrast, "protected" is definitely different from its counterpart in Java code; where a Java protected member is accessible to both subclasses and the package in which the member is defined, Scala chooses to grant access only to subclasses. This means that Scala's version of protected is more restrictive (although arguably more intuitively so) than the Java version.

Where Scala truly steps away from Java code, however, is that access modifiers in Scala can be "qualified" with a package name, indicating a level of access up to which the member may be accessed. For example, if the BizarroMath package wants to grant member access to other members of the same package (but not subclasses), it can use the code in Listing 8 to do so:

Listing 8. Enron's accounting code
package com
{
  package tedneward
  {
    package scala
    {
        // ...
      
      package mathfun
      {
        object BizarroMath
        {
          def bizplus(a : Int, b : Int) = { a - b }
          def bizminus(a : Int, b : Int) = { a + b }
          def bizmultiply(a : Int, b : Int) = { a / b }
          def bizdivide(a : Int, b : Int) = { a * b }
		  
              private[mathfun] def bizexp(a : Int, b: Int) = 0
        }
      }
    }
  }
}

Note the private[mathfun] expression here. In essence, the access modifier is saying that this member is private up to the package mathfun; this means that any member of the package mathfun has access to bizexp but nothing outside of that package does, including subclasses.

The powerful meaning of this is that any package can be declared in the "private" or "protected" declaration all the way up to com (or even _root_ which is an alias for the root namespace, thus essentially making private[_root_] the same thing as "public"). This provides a degree of flexibility in access specification far beyond what the Java language provides.

In fact, Scala offers one more degree of access specification: the object-private specification, illustrated by private[this], which stipulates that the member in question can only be seen by members called on that same object, not from different objects, even if they are of the same type. (This closes a small hole in the Java access specification system that was useful for Java programming interview questions and not much more.)

Note that the access modifiers will have to map on top of the JVM at some level and as a result, some of the subtleties in their definition will be lost when compiled or called from regular Java code. For example, the BizarroMath example above (with the private[mathfun]-declared member bizexp) will generate the class definition in Listing 9 (when viewed with javap):

Listing 9. Enron's accounting library, JVM view
Compiled from "packaging.scala"
public final class com.tedneward.scala.mathfun.BizarroMath
   extends java.lang.Object
{
    public static final int $tag();
    public static final int bizexp(int, int);
    public static final int bizdivide(int, int);
    public static final int bizmultiply(int, int);
    public static final int bizminus(int, int);
    public static final int bizplus(int, int);
}

As is obvious from the second line of the compiled BizarroMath class, the bizexp() method was given a JVM-level access specifier of public which means that the subtle private[mathfun] distinction was lost once the Scala compiler was finished with its access checks. As a result, for Scala code that is intended to be used from Java code, I'd prefer to stick with the traditional "private" and "public" definitions. (Even "protected" will sometimes end up mapping to JVM-level "public," so when in doubt, consult javap against the actual compiled bytecode to be certain of its access level.)

Application

In the preceding article in the series ("Collection types"), when talking about arrays in Scala (Array[T]s to be exact) I said, "obtaining the i'th element of the array" was in fact "another one of those methods with funny names...." As it turns out, although I didn't want to get into the details then, this wasn't exactly true.

OK, I admit it, I lied.

Technically, the use of the parentheses against the Array[T] class is a tad bit more complicated than simply a "method with a funny name"; Scala reserves a particular nomenclature association for that particular sequence of characters (that being the left-parens-right-parens sequence) because that is so often used with a particular intent in mind: that of "doing" something (or in functionalspeak, "applying" something to something).

In other words, Scala has a special syntax (more accurately, a special syntactic relationship) in place for the "application" operator "()". To be precise, Scala recognizes the method called apply() as the method to invoke when said object is invoked using () as the method call. For example, a class that wants to behave as a functor (an object that acts as a function) can define an apply method to provide function- or method-like semantics:

Listing 10. Play that Functor music, code boy!
class ApplyTest
{
  import org.junit._, Assert._  
  
  @Test def simpleApply =
  {
    class Functor
    {
      def apply() : String =
      {
        "Doing something without arguments"
      }
      
      def apply(i : Int) : String =
      {
        if (i == 0)
          "Done"
        else
          "Applying... " + apply(i - 1)
      }
    }

    val f = new Functor
    assertEquals("Doing something without arguments", f() )
    assertEquals("Applying... Applying... Applying... Done", f(3))
  }
}

Curious readers will be wondering what makes a functor different from an anonymous function or closure. As it turns out, the relationship is fairly obvious: The Function1 type in the standard Scala library (meaning a function that takes one parameter) has an apply method on its definition. A quick glance through some of the generated Scala anonymous classes for Scala anonymous functions will reveal that the generated classes are descendants of Function1 (or Function2 or Function3, depending on how many parameters the function takes).

This means that where anonymous or named functions don't necessarily fit the design approach desired, a Scala developer can create a functor class, provide it with some initialization data stored in fields, and then execute it via () without any common base class required (as would be the case for a traditional Strategy pattern implementation):

Listing 11. I said 'play that Functor music, code boy!'
class ApplyTest
{
  import org.junit._, Assert._  

  // ...
  
  @Test def functorStrategy =
  {
    class GoodAdder
    {
      def apply(lhs : Int, rhs : Int) : Int = lhs + rhs
    }
    class BadAdder(inflateResults : Int)
    {
      def apply(lhs : Int, rhs : Int) : Int = lhs + rhs * inflateResults
    }

    val calculator = new GoodAdder
    assertEquals(4, calculator(2, 2))
    val enronAccountant = new BadAdder(50)
    assertEquals(102, enronAccountant(2, 2))
  }
}

Any class that provides the appropriately-argumented apply method will work when called as long as the arguments line up in number and in type.

Conclusion

Scala's packaging, import, and access modifier mechanisms provide the fine degree of control and encapsulation that a traditional Java programmer has never enjoyed. For example, they offer the ability to import select methods of an object, making them appear as global methods, without the traditional drawbacks of global methods; they make working with those methods exceedingly easy, particularly if those methods are methods that provide higher-order functionality, such as the fictitious tryWithLogging function introduced earlier in this series ("Don't get thrown for a loop!").

Similarly, the "application" mechanism allows Scala to hide execution details behind a functional facade such that programmers may not even know (or care) that the thing they are invoking isn't actually a function but an object imbued with significant complexity. The mechanism provides another dimension to Scala's functional nature, one which can certainly be done from the Java language (or C# or C++ for that matter) but not with the degree of syntactic purity Scala provides.

That's it for this installment; until next time, enjoy!


Download

DescriptionNameSize
Sample Scala code for this articlej-scala07298.zip95KB

Resources

Learn

Get products and technologies

  • Download Scala: Start learning it with this series.
  • SUnit: Part of the standard Scala distribution, in the scala.testing package.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=323939
ArticleTitle=The busy Java developer's guide to Scala: Packages and access modifiers
publish-date=07292008