0
\$\begingroup\$

Someone posted a question on http://math.stackexchange.com earlier because their program to tranpose a matrix wasn't working. I copied the code they posted (which was just the transpose method) and added the code necessary to check if it worked on their example matrix and it does, so there's obviously a problem elsewhere in their code. I've posted my code below.

I don't have much programming experience and I suspect my style is unconventional. I implement most of the main program in the constructor, because every time I try to do it in the main method, I get lots of errors because I'm referencing non-static variables and methods from a static context. Maybe there are good reasons for using the static keyword, but I just wanted to write quickly a program to check whether the other person's method was correct and it seemed like the fact that the main method was static was getting in the way, so I just transferred most of the program to the constructor. I have 9 questions (question 8 is actually 2 questions), but question 8 is the most important:

  1. Is my program ok?

  2. Could it be better and if so, why?

  3. What would be the conventional way to write the program?

  4. How would the program be written with the main method in the main method rather than the constructor?

  5. How does having the main method static help?

  6. Someone wrote 'It is good to move code into on or more classes instead of having everything in the main method!'. If I do this, it seems like I get lot of errors because I'm referencing a static variable from a non-static context (since I must at least instantiate one class in the static main method). How would I best overcome this?

  7. Someone else suggested changing everything to static, but that can't be normal, can it?

  8. I know that's a lot of questions. I think the following 2 questions could pin down what I want to find out: where would printMatrix() be in a conventional program (presumably not in the constructor like it is in mine)? If there wouldn't even be such a method in a conventional program, how would the printing of the matrix and its transpose be implemented?

public class Matrix {
    double[][] M = new double[3][3];
    static int mat_size=3;
    public Matrix() {
        M[0][0]=1;
        M[0][1]=0;
        M[0][2]=0;
        M[1][0]=5;
        M[1][1]=1;
        M[1][2]=0;
        M[2][0]=6;
        M[2][1]=5;
        M[2][2]=1;
        System.out.println("M");
        printMatrix();
        transpose();
        System.out.println("M'");
        printMatrix();
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Matrix();
    }

    public void transpose() {

        System.out.println();

        for(int i = 0; i < mat_size; ++i) {
            for(int j = 0; j < i ; ++j) {
                double tmpJI = get(j, i);
                put(j, i, get(i, j));
                put(i, j, tmpJI);
            }
        }
    }

    public void printMatrix() {
        System.out.println();

        for(int i = 0; i < mat_size; ++i) {
            for(int j = 0; j < mat_size ; ++j) {
                System.out.print((int)get(i,j)+" ");
            }
            System.out.println();
        }

    }

    private void put(int i, int j, double d) {
        // TODO Auto-generated method stub
        M[i][j]=d;
    }

    private double get(int i, int j) {
        // TODO Auto-generated method stub
        return M[i][j];
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ Hmm, the code is awful but you did ask the right questions to improve it. However, any answer would have to focus more on Java basics than an actual code review. \$\endgroup\$ – amon Oct 25 '13 at 19:18
  • \$\begingroup\$ lol. +1 for feedback. I did try asking the question on Stack Overflow but it got put on hold as off-topic. @amon \$\endgroup\$ – George Tomlinson Oct 25 '13 at 19:20
  • \$\begingroup\$ Well, it is absolutely off topic over there, a bit less so here. I might just as well attempt an answer. Do you have prior experience with object-oriented programming? If so, which language? \$\endgroup\$ – amon Oct 25 '13 at 19:23
  • \$\begingroup\$ java and C#, but this is java. It was an earlier version of the question, which didn't have questions 6-8, but still, I didn't see how it failed to meet the requirement they cited: "Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself." I might take a look at a sample program on the Java Outside In Website: that might improve my understanding or even perfect it. \$\endgroup\$ – George Tomlinson Oct 25 '13 at 19:30
  • 1
    \$\begingroup\$ I've found the info I needed by looking at a sample program on the Java Outside In website. The problem was I was just putting printMatrix(); in the main method rather, than instantiating a Matrix Ma and then putting Ma.printMatrix(); I did know this: it's just been a while since I've done any java, except for a program I wrote using WindowBuilderPro, which seemed to put the main program in the constructor, like I did. \$\endgroup\$ – George Tomlinson Oct 25 '13 at 20:15
6
\$\begingroup\$

Is my program ok?

Well, I guess it works. However, it can be structured far better.

Basic Object Oriented Programming in Java

In Java (and all of this is also true for C#), every object has a type which is represented by a so-called class. We can define operations on each type, which we call methods. Some methods operate on an individual instance of a type. Other operations are not inherently bound to instances, and operate on the whole class. We call this second kind of operations static methods.

Likewise, data can either belong to a specific instance, or is shared across all instances, in which case it's called static. Every method (static and non-static) can refer to static data, because that is shared. However, static methods cannot reference instance data, because a static method can't tell from which instance you'd like the data – and sometimes, no instance may exist at all. Static methods are mostly useful for general helpers.

It is really important to grok the difference between static and non-static methods in order to use Java effectively (the same holds for any other class-based programming language).

Meaning of the main

What is the main and why do we need it? The main originated as a feature of the C programming language. It defines an entry point into the program. At startup, your classes are initialized and then the main methods gets called with the command line arguments in an array as parameter. The main method doesn't return a value (it is void), because returning from it aborts the process.

The main is static because it's a helper method to get your control flow going – it is not coupled to any instance. It is therefore common in a main to create an instance of the surrounding class, and work with that.

Constructors

A constructor is a special method used to initialize a fresh instance. All it usually does is assigning its arguments to the corrects data fields of the instance. It isn't recommended to do the actual work here – put that into another method.

In your code, everything after the initialization can be moved into the main method where it belongs. Note that methods are invoked on a specific instance with the dot operator. The main now looks like:

public static void main(String[] args) {
    Matrix matrix = new Matrix();

    System.out.println("M");
    matrix.printMatrix();

    matrix.transpose();

    System.out.println("M'");
    matrix.printMatrix();
}

Now we are already getting into the code review zone of this answer.

Design spotlights

A method should do one thing only and – no “and”!

Every method should do one thing only. Exaggerated: While a description of your method uses the word “and”, it is doing to much. Split the functionality into more methods. For example, your transpose method does this: “It prints an empty line and swaps columns with rows of the matrix”. Is printing an empty line really necessary in a method called “transpose”?

The same goes for your version of the constructor: “It initialized the matrix and prints it out and transposes it and prints it out again” – but we already cleaned that up above.

Composability

An advantage of having each method doing one concept only makes it easier to compose them in a different way to do new things. There are a few important things that can make this easier:

Don't hardcode stuff – parameterize it

Don't hardcode things that aren't universal constants. Actually, don't hardcode things at all. For example, you assign static int mat_size = 3 – but what if I want to use a 5×5 matrix? You don't provide any functionality that actually depends on the number three. Instead, the size is a property of each instance. Therefore, we'll remove the static modifier, and initialize it in the constructor.

The constructor is another example of hardcoding: The whole test matrix is specified there. It would be better to move that data out of the constructor, and have the constructor take an array as argument instead. I'd rather initialize a new matrix like:

// array literals :) – however, I'm unsure about the exact syntax
double[3][3] testData = { { 1, 0, 0 },
                          { 5, 1, 0 },
                          { 6, 5, 1 } };
Matrix matrix = new Matrix(3, testData);

We can now initialize a matrix with arbitrary other values, not only that specific data set.

Immutability

When objects don't change their internal state over time, programs often get a bit simpler. Mathematics does this as well: When we transpose a matrix M, the M still has its old values. The transposition produces a new and distinct matrix, which we can call M' (you even do this in your printout). There is no reason our transpose method shouldn't return a new matrix either. Now we get something along the lines of:

public Matrix transpose() {
    double[size][size] newData = new double[size][size];

    for(int i = 0; i < size; i++) {
        for(int j = 0; j < i ; j++) {
            newData[i][j] = get(j, i);
            newData[j][i] = get(i, j);
        }
        newData[i][i] = get(i, i);
    }

    return new Matrix(size, newData);
}

This changes our main:

public static void main(String[] args) {
    Matrix matrix = new Matrix();

    System.out.println("M");
    matrix.printMatrix();

    Matrix transposed = matrix.transpose();

    System.out.println("M'");
    transposed.printMatrix();
}

No side effects

When a method has any effect except returning some data, it has side effects. Such effects might be changing the state of some object (thus voiding the point about immutability), or printing out some data. These side effects make it more difficult to combine these methods in a new way.

Specifically, printMatrix is an offender here. Also because it can't be changed to print to anything other than System.out. What happens when I want a string representation to display on a GUI? I can't simply use your awesome Matrix class :(

Here, it would be better to implement the toString() function, which returns a string representation of your object. There is already a default implementation for every object, but we can override it. Then, our main will look like:

public static void main(String[] args) {
    Matrix matrix = new Matrix();

    System.out.println("M");
    System.out.println(matrix.toString());

    Matrix transposed = matrix.transpose();

    System.out.println("M'");
    System.out.println(transposed.toString());
}

That is more ugliness here, but massively clears up the rest of your code. The toString might be:

public String toString() {
    StrinBuilder sb = new StringBuilder();
    for(int i = 0; i < size; i++) {
        for(int j = 0; j < size ; j++) {
            sb.append(" " + (int) get(i, j));
        }
        sb.append("\n");
    }
    return sb.toString();
}
// apologies if I mixed up the StringBuilder API

A closing note on naming things

In Java, names are written in camelCase. Class names are capitalized. Any other names start with a lowercase letter: Matrix refers to a class, while matrix is a local variable, field, or method. As an exception, constants are written all-uppercase and seperated by underscores. Because for normal names the parts of a word are distinguished by capitalization, no underscores are ever used.

It is advisable to use meaningful names like matrix or transformedMatrix, not shortcuts like m or t or m_. Choosing longer names makes it easier to read a piece of code for someone who isn't used to your shortcut notation.

\$\endgroup\$
  • \$\begingroup\$ We touched on almost the exact same issues. You got me on transpose returning a new Matrix. I didn't even consider that. I like it! \$\endgroup\$ – Origineil Oct 25 '13 at 20:48
2
\$\begingroup\$

When referencing my example in the text below, refer to my implementation.

1. Is my program ok?

If it runs, you are doing better than most people. A great place to start is the Code Conventions for Java. Other than that, its just a little cleanup and application of object oriented programming (OOP) principles.

5. How does having the main method static help?

The method marks the entry point for executing the program. Here are a few posts regarding the topic: First, Second. Others are most certainly available.

7. Someone else suggested changing everything to static, but that can't be normal, can it?

The keyword static only fits a select few scenarios and is best avoided for all but those cases. You can search for plenty of posts regarding the topic.

6. ... use more classes ... How would I best overcome [static]?

A way to drop the static context is to have your top-level java class implement the Runnable interface and launch a new thread. From this point, you can have any number of classes you like. In the example, my Matrix has no concept of how to run. The MatrixRunner assumes that responsibility. Since the runner is the direct link to the main method it eats the static context and allows any further coding to be static free.

8. ... where would printMatrix() be ... where would transpose be implemented

Since the actions involve a matrix object, it should be encapsulated within the class. In my example, I chose to allow the override of .toString() represent my print function. Thus, you would just call whatever variation of System.out.print you like on some instantiated Matrix object. Similarly, transpose is a action on a matrix; actions are represented as methods, thus transpose becomes a method within the matrix class.

\$\endgroup\$
  • \$\begingroup\$ How does the toString() method get called? I don't see any explicit reference to it. \$\endgroup\$ – George Tomlinson Oct 26 '13 at 11:43
  • 1
    \$\begingroup\$ It gets called "behind the scenes" by the print statement. \$\endgroup\$ – Origineil Oct 26 '13 at 18:21
  • \$\begingroup\$ +1 because it's useful. I've seen this with a paint() method in another program. How can I know which methods operate this way? \$\endgroup\$ – George Tomlinson Oct 26 '13 at 20:29
  • 1
    \$\begingroup\$ toString(), equals(), and hashCode() are the three primary methods that come to mind \$\endgroup\$ – Origineil Oct 27 '13 at 7:06
0
\$\begingroup\$

I've found the info I needed by looking at a sample program on the Java Outside In website. The problem was I was just putting

printMatrix();

in the main method rather, than instantiating a Matrix Ma and then putting

Ma.printMatrix();

I did know this: it's just been a while since I've done any java, except for a program I wrote using WindowBuilderPro, which seemed to put the main program in the constructor, like I did.

\$\endgroup\$

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.