1
\$\begingroup\$

I am a Scala newbie and attempting to improve my skills. I have decided to be playful about it and work to do a conversion of the spaceinvaders game supplied with the LWJGL. It consists of 10 Java classes of which I have loaded, compiled and successfully ran from Eclipse.

My first Scala step is to replace the Sprite.java class with a SpriteNew.scala class. I have completed the first pass (see below). However, I have several questions:

  1. How do I make class SpriteNew only have instances created by Object SpriteNew.create? In Java, I would just make the constructor private. Since I cannot do that, I am wondering if I am missing how I must approach a problem like this different than how I do so with Java.
  2. In Object SpriteNew, I don't like how I used a var to temporarily define a field so that I can reference/return it in the final line of the load method. Are there any better ways to write this method keeping the intention of isolating the Java/null-ness/Exception-ness?

Any feedback, coaching, guidance, suggestions you can give me on this would be greatly appreciated.

For brevity, I have stripped the comments out of the original Sprite.java class (go here if you would like to see the original unmodified source for Sprite.java):

package org.lwjgl.examples.spaceinvaders;

import java.io.IOException;

import static org.lwjgl.opengl.GL11.*;

public class Sprite {

    private Texture texture;

    private int         width;

    private int         height;

    public Sprite(TextureLoader loader, String ref) {
        try {

            texture = loader.getTexture(ref);
            width = texture.getImageWidth();
            height = texture.getImageHeight();
        } catch (IOException ioe) {
            ioe.printStackTrace();
            System.exit(-1);
        }
    }

    public int getWidth() {
        return texture.getImageWidth();
    }

    public int getHeight() {
        return texture.getImageHeight();
    }

    public void draw(int x, int y) {
        glPushMatrix();

        texture.bind();

        glTranslatef(x, y, 0);

        glBegin(GL_QUADS);
        {
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);

            glTexCoord2f(0, texture.getHeight());
            glVertex2f(0, height);

            glTexCoord2f(texture.getWidth(), texture.getHeight());
            glVertex2f(width, height);

            glTexCoord2f(texture.getWidth(), 0);
            glVertex2f(width, 0);
        }
        glEnd();

        glPopMatrix();
    }
}

And here is my first pass at the Scala version as a Factory (using both object and class):

package org.lwjgl.examples.spaceinvaders

import java.io.IOException
import org.lwjgl.opengl.GL11._

object SpriteNew {
  def create (textureLoader: TextureLoader, resourceName: String): SpriteNew = {
    def load: Texture = {
      var texture: Texture = null
      try {
        texture = textureLoader.getTexture(resourceName)
      }
      catch
      {
        case ioe: IOException => {
          println(ioe.getStackTraceString)
          System.exit(-1)
        }
      }
      texture
    }

    val texture = load
    new SpriteNew(texture)
  }
}

class SpriteNew (val texture: Texture) {
  val width = texture.getImageWidth()
  val height = texture.getImageHeight()
  def draw(x: Int, y: Int) {
    glPushMatrix
      texture.bind()
      glTranslatef(x, y, 0)
      glBegin(GL_QUADS)
        glTexCoord2f(0, 0)
        glVertex2f(0, 0)

        glTexCoord2f(0, texture.getHeight())
        glVertex2f(0, height)

        glTexCoord2f(texture.getWidth(), texture.getHeight())
        glVertex2f(width, height);

        glTexCoord2f(texture.getWidth(), 0)
        glVertex2f(width, 0)
      glEnd
    glPopMatrix
  }
}

Update (2011/Nov/22 - 15:00 CDT):

I am incorrect in question 1 about not being able to make the constructor private. Placing the keyword "private" after the Scala class name and before the constructor parameter list allows the class to remain public while the constructor become private. In other words, snippet above "class SpriteNew (val texture: Texture) {" would be altered to become "class SpriteNew private (val texture: Texture) {"

\$\endgroup\$
  • \$\begingroup\$ I have made huge progress with Scala since I initially posted this. To see an even more effective way to isolate the new operator and to easily implement case class instance caching, have a look at this answer I posted: codereview.stackexchange.com/a/98367/4758 \$\endgroup\$ – chaotic3quilibrium Jul 28 '15 at 17:42
3
\$\begingroup\$

Question 1: You answered it yourself.

Question 2: try / catch in Scala is an expression. Also, sys.exit is of type Nothing, which is the bottom type, a sub-type of every other type, hence it will not throw off the type inference. So your create method can be

  def create (textureLoader: TextureLoader, resourceName: String) = new SpriteNew (
    try textureLoader.getTexture(resourceName)
    catch { case ioe: IOException => 
      println(ioe.getStackTraceString)
      sys.exit(-1)
    } 
  )

It would also be more usual to use apply for the creation method name (instead of create), so that you can make a new instance with SpriteNew(a, b).

I like to avoid wasted vertical space in my layout. It's not great if you're paid by lines of code written, but IMO it's much easier to see what's going on if the code isn't all spread out, and you can fit more on the page so you don't have scroll back and forth.

\$\endgroup\$
  • \$\begingroup\$ Awesome! Tysvm for your response. It's so obvious, now that you have written it. And I so didn't see it when I was working with the problem. \$\endgroup\$ – chaotic3quilibrium Nov 27 '11 at 1:22
  • 1
    \$\begingroup\$ @chaotic3quilibrium You're welcome. Re-throwing the exception looked a bit strange so I thought "wouldn't it be good if System.exit were of type Nothing". A quick look at the documentation reveals that indeed the Scala version, sys.exit is! So I've updated the answer. \$\endgroup\$ – Luigi Plinge Nov 27 '11 at 4:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.