Setting up Scala for IntelliJ is very easy

Photo by Manuel Nägeli on Unsplash

You can certainly write Scala programs in a plain text editor like Vim and then compile and test them on the command line.

But if you already have a Java integrated development environment (IDE) like IntelliJ or NetBeans, and since Scala compiles to bytecode for the Java Virtual Machine, wouldn’t it make more sense to use an IDE for Scala programming?

You certainly can, but the setup is much easier for IntelliJ than for NetBeans or Eclipse.

Getting the Scala for IntelliJ plugin

It doesn’t actually matter if your computer already has or doesn’t have the Scala binaries or the Simple Build Tool (now known as “sbt”). Download and install the plugin and it’ll all be taken care of. Most of it anyway.

I have set up Scala for IntelliJ on both a Windows 10 laptop and a Mac OS X laptop. On the Windows laptop, I had already been compiling some simple Scala programs on the command line before getting the IntelliJ plugin. On the MacBook, I hadn’t.

On IntelliJ running under Mac OS X, go to the IntelliJ Idea menu and choose Preferences… On IntelliJ running under Windows, if I recall correctly, it’s Edit > Preferences…

Then select Plugins. Maybe the Scala for IntelliJ plugin will be the very first of the featured plugins. But if it’s not, you can search for it.

A selection of featured plugins on the IntelliJ Marketplace. The Scala plugin is the very first, it’s free, and it’s already installed on the computer on which this screenshot was taken.

As a matter of fact, when I installed IntelliJ on my MacBook a few months ago, Scala for IntelliJ was a featured plugin at install time. So I have had Scala on IntelliJ on that machine from the get-go.

Once you’ve found the Scala plugin, click Install, and the rest of the process is fairly self-explanatory. Depending on your Internet connection, downloading the plugin might be the most time-consuming part of this process.

But just because you’ve downloaded and installed a plugin for a programming language doesn’t necessarily mean you can now start writing and running programs in that language.

That’s a lesson I learned the hard way from the Kotlin plugin for NetBeans (that plugin still is, at the time of this writing, in a gray area, not being worked on by JetBrains, not quite turned over to Apache).

Getting IntelliJ and Scala in one click

According to JetBrains, it takes at least fifteen minutes and more than a hundred clicks to install IntelliJ and then the Scala for IntelliJ plugin. That doesn’t sound too bad if it’s just for one computer, your own computer.

But if you need it on several computers (such as for a group of students), it would be nice if there was a way to install a bundle with just one click on each computer, and maybe further automate the process from a main computer.

Turns out there is a one-click bundle from JetBrains on GitHub (I made sure to check the account is marked as “verified” by GitHub). So you can be sure the bundle includes an up-to-date version of IntelliJ IDEA Community Edition.

I have not actually tested the bundle, but I have never had reason to doubt assertions of fact from JetBrains. And also JetBrains developer Pavel Fatin is the one who let me know about the bundle (through Twitter).

Keep in mind, however, that the constituent parts of the bundle (like sbt, which is not from JetBrains) “are governed by their separate license agreements, and do not form a single product.”

Verifying that the plugin actually works

To test that the Scala for IntelliJ plugin actually works, we need a simple Scala program. The classic Hello World program will do just fine. We’re going to create a new project called “SimpleExercises”.

You should see Scala as an option in the New Project dialog. Although sbt is recommended by IntelliJ, I don’t recommend it, my computer got stuck. That could be because maybe sbt needs to be downloaded and installed separately.

After waiting for almost five minutes (actually I was doing other things), I stopped the process, closed the project and deleted the project folder.

I tried again, chose IDEA Project this time around, and I was soon on to the next step.

Select Scala on the left, IDEA on the right. I don’t recommend the sbt option. No comment on Lightbend at this time.
Check the project location, JDK, Scala SDK, click Finish.

In the src folder, let’s create a helloworld package. Then right-click in that package. You should see the option to create a Java class, a Kotlin class, a Scala class, etc. Choose Scala class and name it “HelloWorld”.

IntelliJ gives Scala classes an icon that is very similar to the icon it uses for Java classes, except that the icon incorporates the Scala logo. At this point you should have an empty HelloWorld class.

Replace that with this:

package helloworldobject HelloWorld {  def main(args: Array[String]): Unit = {
println("Hello, world!")
}
}

You don’t actually have to type “: Unit = ”, IntelliJ will fill it in for you if you leave it out. But you’re certainly welcome to just copy and paste the HelloWorld object from here.

It might be instructive to actually type the Hello World program, to see all the auto-complete help IntelliJ provides. First, change “class HelloWorld” to “object HelloWorld”.

IntelliJ just after you change the Scala class to a Scala object, but before you type in the main procedure.

Notice that the Scala class icon chances to an object icon. In Scala, an “object” is kind of like a Java class with everything static. I suppose in informal Java parlance that would be a “static class,” but IntelliJ has no need to provide a separate icon for such a class.

Once you have the main procedure typed in, there should be two green play buttons. Click on either one of them to run the program.

The program should run, you should see “Hello, world!” in the console. IntelliJ also tells us the process finished normally, with exit code 0. There should be no warnings nor errors in the console.

Now let’s try something just a little bit more involved: the CurrencyAmount class. In an actual project, you’d probably want to use something like the Scala equivalent of JSR-354. But for an exercise, it’s fine to reinvent this wheel.

In the src folder, create a currency package. In that package, create a CurrencyAmount class and a CurrencyConversionNeededException class. We need that exception in case someone tries to do something like add a million yen to a thousand euros, for example.

Here’s the source for the first draft of CurrencyAmount:

package currencyimport java.util.Currencyobject CurrencyAmount {  val UNITED_STATES_DOLLARS: Currency = Currency.getInstance("USD")}class CurrencyAmount(cents: Long,
currencyID: Currency =
CurrencyAmount.UNITED_STATES_DOLLARS)
extends Ordered[CurrencyAmount] {
val amountInCents: Long = cents
val currencyIdentifier: Currency = currencyID
// THIS WILL FAIL SOME OF THE TESTS
override def toString: String = {
val pointPlace = this.currencyIdentifier.getSymbol.length +
Math.ceil(Math.log10(Math.abs(this.amountInCents))).toInt -
this.currencyIdentifier.getDefaultFractionDigits +
(if (this.amountInCents < 0) 1 else 0)
(this.currencyID.getSymbol +
this.amountInCents.toString).patch(pointPlace, ".", 0)
}
override def equals(obj: Any): Boolean = obj match {
case obj: CurrencyAmount =>
this.currencyIdentifier == obj.currencyIdentifier &&
this.amountInCents == obj.amountInCents
case _ => false
}
override def hashCode: Int = this.currencyID.hashCode * 65536 +
this.amountInCents.toInt
// STUB TO FAIL THE TEST
def +(summand: CurrencyAmount): CurrencyAmount = new
CurrencyAmount(0, this.currencyID)
// STUB TO FAIL THE TEST
def unary_- = new CurrencyAmount(0, this.currencyID)
def -(subtrahend: CurrencyAmount): CurrencyAmount = this +
(-subtrahend)
// STUB TO FAIL THE TEST
def *(multiplier: Int): CurrencyAmount = new
CurrencyAmount(0, this.currencyID)
// STUB TO FAIL THE TEST
def /(divisor: Int): CurrencyAmount = new
CurrencyAmount(0, this.currencyID)
override def compare(that: CurrencyAmount): Int = {
val diff = this - that
diff.amountInCents match {
case 0 => 0
case n if n < 0 => -1
case _ => 1
}
}
}

Notice the mixed class/object icon. In such a class, the object is often referred to as a “companion object.”

If you take a peek at the folder where IntelliJ stores bytecode class files, you should see the files CurrencyAmount.class and CurrencyAmount$.class (I’m pretty sure the latter is for companion object).

What we have so far should fail most of the tests we’ll write later. Now for the exception:

package currencyclass CurrencyConversionNeededException(msg: String,
amtA: CurrencyAmount,
amtB: CurrencyAmount)
extends RuntimeException(msg) {
val amountA: CurrencyAmount = amtA
val amountB: CurrencyAmount = amtB
val exchangeRate: Double = 0.5 // TODO: Get actual exchange rate
def exchangeAToB: CurrencyAmount = {
val exchangeAmount = Math.floor(exchangeRate *
amountA.amountInCents).toLong
new CurrencyAmount(exchangeAmount, amountB.currencyIdentifier)
}
def exchangeBToA: CurrencyAmount = {
val exchangeAmount = Math.floor(amountB.amountInCents /
exchangeRate).toLong
new CurrencyAmount(exchangeAmount, amountA.currencyIdentifier)
}
}

As you might already know, there are no checked exceptions in Scala, so we could just as easily subclass CurrencyConversionNeededException from Exception rather than RuntimeException.

However… if we wanted this to interoperate with Java classes, we’d need the @throws annotation, or else we’d run into some problems at compile-time.

And in any case, if this was a Java project, you wouldn’t want to bog someone down with constantly having to check all amounts are in U. S. dollars or whatever currency you’re using as your primary currency.

So that’s why we subclass that one from RuntimeException. Also, we don’t need to write “super(msg)” because that’s taken care of by “extends RuntimeException(msg)”.

It would be too much of a digression to ponder how exactly this exception obtains the appropriate exchange rate, so we’ll just have a placeholder exchange rate of 0.5 from the A currency to the B currency.

In any case, even in a real life project, this exception would be limited to the exchange rate at the time it was thrown. The program catching the exception might decide to wait for a more favorable exchange rate.

Okay, next, we make sure we have stubs for all the monetary arithmetic functions that we’re going to need. Plus, minus, times, divides, for sure. Maybe also percentage and permillage.

Also make sure CurrencyAmount implements Ordered[CurrencyAmount] (actually “extends” or “with” in Scala). That’s a lot like the interface Comparable[CurrencyAmount] in Java (which we could certainly use instead if we wanted to), but much better.

For one thing, it will allow us to compare money amounts (provided they’re of the same currency) directly with the greater than and less than operators, rather than with the clunky compareTo() of Comparable<T>.

This will all need testing. Let’s go ahead and add a test content root to our project with File > Project Structure… Don’t worry about creating the packages to correspond to src, let IntelliJ take care of that.

With CurrencyConversionNeededException upfront on the editor, choose from the menu Code > Generate… and then select Generate Test.

You’re probably thinking that this is going to create a test class in Java, not Scala. And you’d be right if you’re thinking that. This is the simplest way that I know of to connect JUnit to an IntelliJ project, be it Java, Scala or Kotlin.

Also, it gives me an opportunity to show more clearly the interoperability of Java and Scala. There is such a thing as ScalaTest. But… if you know and like JUnit, you might as well use it.

You can use JUnit to test a Scala class, and you can write the test class in either Java or Scala (probably Kotlin, too).

For the exception, we’re only going to test getMessage() at this point, and we’re going to only slightly change what IntelliJ wrote for us in Java. Also create instances of CurrencyAmount of different currencies (such as euros and dollars).

    @Test
public void testGetMessage() {
System.out.println("getMessage");
String message = "Conversion needed for euros, dollars";
try {
throw new CurrencyConversionNeededException(message,
euroAmount, dollarAmount);
} catch (CurrencyConversionNeededException currConvExc) {
assertEquals(message, currConvExc.getMessage());
}
}

Now we move on to testing CurrencyAmount. We’ll write the test class in Scala. There should already be a currency package inside the test content root. Now create a new Scala class in that package, naming it CurrencyAmountTest.

Here is the first draft of CurrencyAmountTest:

package currencyimport java.util.Currencyimport org.junit.Test
import org.junit.Assert._
class CurrencyAmountTest { private val bucks = Currency.getInstance("USD") @Test def testToString(): Unit = {
println("toString")
val someAmount = new CurrencyAmount(5322L, bucks)
val expected = "$53.22"
val actual = someAmount.toString
assertEquals(expected, actual)
}
@Test def testToString8Cents(): Unit = {
val someAmount = new CurrencyAmount(8L, bucks)
val expected = "$0.08" // Or, if you prefer, "$.08"
val actual = someAmount.toString
assertEquals(expected, actual)
}
@Test def testToString47Cents(): Unit = {
val someAmount = new CurrencyAmount(47L, bucks)
val expected = "$0.47" // Or, if you prefer, "$.47"
val actual = someAmount.toString
assertEquals(expected, actual)
}
@Test def testToStringPowerOfTen(): Unit = {
val someAmount = new CurrencyAmount(100000000L, bucks)
val expected = "$1000000.00"
val actual = someAmount.toString
assertEquals(expected, actual)
}
@Test def testToStringNegativeAmounts(): Unit = {
val someAmount = new CurrencyAmount(-380L, bucks)
val expected = "$-3.80" // Or, if you prefer, "-$3.80"
val actual = someAmount.toString
assertEquals(expected, actual)
}
@Test def testEquals(): Unit = {
println("equals")
val someAmount = new CurrencyAmount(5322L, bucks)
val sameAmount = new CurrencyAmount(5322L, bucks)
assertEquals(someAmount, sameAmount)
val diffAmount = new CurrencyAmount(239L, bucks)
assertNotEquals(someAmount, diffAmount)
}
@Test def testHashCode(): Unit = {
println("hashCode")
val startNum = Math.floor(Math.random() * 1000).toInt
val len = Math.floor(Math.random() * 1000).toInt
val endNum = startNum + len - 1
val amounts = for (cents <- startNum to endNum)
yield new CurrencyAmount(cents, bucks)
val hashes = amounts.map(_.hashCode).toSet
assertEquals(len, hashes.size)
}
@Test def testPlus(): Unit = {
println("+")
val summandA = new CurrencyAmount(4799L, bucks)
val summandB = new CurrencyAmount(5201L, bucks)
val expected = new CurrencyAmount(10000L, bucks)
val actual = summandA + summandB
assertEquals(expected, actual)
}
@Test(expected = classOf[CurrencyConversionNeededException])
def testCannotAddDifferentCurrencies(): Unit = {
val euros = Currency.getInstance("EUR")
val summandA = new CurrencyAmount(4799L, euros)
val summandB = new CurrencyAmount(5201L, bucks)
print("Attempting to add " + summandA.toString + " to " +
summandB.toString)
val result = summandA + summandB
println(" somehow gave result " + result.toString)
}
@Test def testMinus(): Unit = {
println("-")
val minuend = new CurrencyAmount(8998L, bucks)
val subtrahend = new CurrencyAmount(8489L, bucks)
val expected = new CurrencyAmount(509L, bucks)
val actual = minuend - subtrahend
assertEquals(expected, actual)
}
@Test def testCompare(): Unit = {
println("compare")
val negBal = new CurrencyAmount(-13400L, bucks)
val amountA = new CurrencyAmount(4359L, bucks)
val amountB = new CurrencyAmount(70329L, bucks)
val amountC = new CurrencyAmount(12500000000L, bucks)
var assertionMessage = negBal.toString +
" should be found to be less than " + amountA.toString
assertTrue(assertionMessage, negBal < amountA)
assertionMessage = amountA.toString +
" should be found to be less than " + amountB.toString
assertTrue(assertionMessage, amountA < amountB)
assertionMessage = amountB.toString +
" should be found to be less than " + amountC.toString
assertTrue(assertionMessage, amountB < amountC)
assertionMessage = amountC.toString +
" should be found to be more than " + amountB.toString
assertTrue(assertionMessage, amountC > amountB)
assertionMessage = amountB.toString +
" should be found to be more than " + amountA.toString
assertTrue(assertionMessage, amountB > amountA)
assertionMessage = amountA.toString +
" should be found to be more than " + negBal.toString
assertTrue(assertionMessage, amountA > negBal)
}
@Test def testCompareThroughCollectionSort(): Unit = {
val negBal = new CurrencyAmount(-13400L, bucks)
val zero = new CurrencyAmount(0L, bucks)
val amountA = new CurrencyAmount(4359L, bucks)
val amountB = new CurrencyAmount(70329L, bucks)
val amountC = new CurrencyAmount(12500000000L, bucks)
val unsortedList = List(amountA, negBal, amountC, zero, amountB)
val expected = List(negBal, zero, amountA, amountB, amountC)
val actual = unsortedList.sorted
assertEquals(expected, actual)
}
}

I choose to name the procedure to test the addition function “testPlus()” rather than just “+()” because I think the latter would be needlessly confusing. Sometimes it’s good to be verbose. Now let’s make testPlus() pass.

Notice also we have a test to make sure one can’t just add a million yen to a thousand dollars or something like that without a currency conversion (and potentially a currency conversion fee).

I hope that at this point you’re feeling like you want to work on your own Scala projects in IntelliJ. Or you might want to carry this currency package exercise to its logical conclusion.

And if you still want more exercises, I suggest you create the following classes: Person, Company, SavingsAccount, CheckingAccount, Deposit, Withdrawal, Fee, Refund, etc. Make appropriately named packages for these classes.

You can also add Scala classes to existing IntelliJ Java projects. There are a couple of hoops to jump through, but nothing terribly difficult. Beware of JAR bloat, though.

To summarize this section: installing Scala on IntelliJ is very easy. There are a few wrinkles to actually using it, but nothing by any means prohibitive.

What about NetBeans, Eclipse?

There is a Scala plugin for NetBeans. However, I’ve run into several difficulties just trying to download and install it. Maybe I should just wait for the plugin to pass verification for Apache NetBeans 11.1.

As for Eclipse, if I’m understanding correctly, the only available option is Scala IDE for Eclipse. It bundles a slightly outdated version of the Eclipse Java IDE with the Scala IDE for Eclipse per se, Scala binaries for two or three versions of Scala, ScalaTest and a few other things.

Problem is, that package is from an unidentified developer, according to macOS Mojave. I can certainly change or override my MacBook’s security settings, but I would rather not.

Hopefully in a few months I can post an update on Scala for NetBeans and/or Eclipse.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.