Hi all! In a course I am T.A.ing, one of the students submitted this program as their assignment and I did not fully understand why it wasn't working:

public class Alphabet
{
    public static void main (String[] args)
    {
        char alphabet= 'A';
        
        System.out.println("The following is the English alphabet.");
        while (alphabet<='Z')
        {
            System.out.print(alphabet + " ");
            alphabet = alphabet++;
        }
    }
}

The code above printed out an infinite number of 'A's on the screen. Clearly, the alphabet variable is not being updated despite the "alphabet++." If it is changed to alphabet = ++alphabet (preincrementation instead of postincrementation) the code works correctly. But I still don't fully understand -- alphabet = alphabet++ should still result in alphabet being updated by the end of the loop's iteration. So why is alphabet not updating despite the ++, which means alphabet = alphabet + 1?

maybe there you can find ...

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class GenerateProgresId {

    private static String[] aChar = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
    private static List<String> listA;

    public GenerateProgresId() {
        listA = new ArrayList<String>();
        listA.addAll(Arrays.asList(aChar));
        System.out.println("From List");
        System.out.println(GenerateProgresIdList(null));
        System.out.println(GenerateProgresIdList(""));
        System.out.println(GenerateProgresIdList("B"));
        System.out.println(GenerateProgresIdList("AA"));
        System.out.println(GenerateProgresIdList("AZ"));
        System.out.println(GenerateProgresIdList("ZY"));
        System.out.println(GenerateProgresIdList("ZZ"));
        System.out.println("From String[]");
        System.out.println(GenerateProgresIdString(null));
        System.out.println(GenerateProgresIdString(""));
        System.out.println(GenerateProgresIdString("B"));
        System.out.println(GenerateProgresIdString("AA"));
        System.out.println(GenerateProgresIdString("AZ"));
        System.out.println(GenerateProgresIdString("ZY"));
        System.out.println(GenerateProgresIdString("ZZ"));
    }

    public static String GenerateProgresIdList(String str) {
        int lastChar = aChar.length - 1;
        String retStr = "AA";
        if (str != null) {
            if (str.length() > 0) {
                if (str.length() == 1) {
                    retStr = str + aChar[0];
                } else if (str.length() == 2) {
                    int stChar = listA.indexOf(str.substring(0, 1));
                    int ndChar = listA.indexOf(str.substring(1, str.length()));
                    if ((stChar != lastChar) || (ndChar != lastChar)) {
                        if (ndChar == lastChar) {
                            retStr = listA.get(stChar + 1) + listA.get(0);
                        } else {
                            retStr = listA.get(stChar) + listA.get(ndChar + 1);
                        }
                    }
                }
            }
        }
        return retStr;
    }

    public static String GenerateProgresIdString(String str) {
        String lastChar = aChar[aChar.length - 1];
        String retStr = "AA";
        if (str != null) {
            if (str.length() > 0) {
                if (str.length() == 1) {
                    retStr = str + aChar[0];
                } else if (str.length() == 2) {
                    if ((!str.substring(0, 1).equals(lastChar)) || (!str.substring(1, str.length()).equals(lastChar))) {
                        String pos1 = str.substring(0, 1);
                        String pos2 = str.substring(1, str.length());
                        if ((pos2).equals(lastChar)) {
                            int heplInt = 0;
                            for (int i = 0; i < aChar.length; i++) {
                                if (aChar[i].equals(pos1)) {
                                    heplInt = i + 1;
                                    break;
                                }
                            }
                            retStr = aChar[heplInt] + aChar[0];
                        } else {
                            int heplInt = 0;
                            for (int i = 0; i < aChar.length; i++) {
                                if (aChar[i].equals(pos2)) {
                                    heplInt = i + 1;
                                    break;
                                }
                            }
                            retStr = pos1 + aChar[heplInt];
                        }
                    }
                }
            }
        }
        return retStr;
    }

    public static void main(String[] args) {
        GenerateProgresId gpi = new GenerateProgresId();
    }
}

a++ returns the value a, then adds 1 to it, so
a = a++ always leaves a unchanged
simply a++ will do, or
a = ++a; (increments before using value)

The value of the postfix increment expression is the value of the variable before the new value is stored.

Java Language Ref15.14.2 Postfix Increment Operator ++

Edited 5 Years Ago by JamesCherrill: n/a

The answer above me (mKorbel at the time I was replying) is the definition of awesome.

However, I'll have a pretty guessish go. I pulled this off oracle's site:

The code result++; and ++result; will both end in result being incremented by one. The only difference is that the prefix version (++result) evaluates to the incremented value, whereas the postfix version (result++) evaluates to the original value.

So the variable's evaluated at its original value, instead of origValue+1.

Edited 5 Years Ago by Buffalo101: n/a

James - that's the answer that came to me, but why doesn't it then perform the increment?

Clearly the original writer of the code was confused, there's no need to have the assignment there. However, it should work all the same. Suppose it were

b = a++;

You'd expect the following steps:

get a left-hand value for b
get a right hand value for a
assign the latter to the former
increment a

... so you'd end up with

b = a; 
a = a++;

Now why doesn't it work that way when it's a= a++?

That is:

assign a to itself (no-op)
increment a

So this does look weird to me.


However the real lesson here is, don't use increment operators in the right hand side of an expression. Even if it works the way you expect it to, it's still more difficult to read for no good reason.

a = a++;
I believe the sequence is:
evaluate the expression a++ (returns the initial value of a)
increment the value of a
assign the value of the expression to a

That would make sense - it's a reasonable order of operations for the compiler. Totally non-intuitive out come in this case, of course, but I guess you really shouldn't ever run into this case unless you're teaching first-year Java and a student hands you this.

non-inituitive it really is. But then if it worked any other way this very common pattern wouldn't work...

String[] data = new String[99];
int count = 0;

void addStringToArray(String s) {
   data[count++] = s;
}

Edited 5 Years Ago by JamesCherrill: n/a

Oh, no, that works fine as long as postincrement increments count after evaluating it for its R-value. What's counterintuitive about the kvass' example is that it evaluates, increments, and then assigns. What I'd expect would be evaluate, assign, and then increment - which would work in your case and kvass' as well.

Oh, no, that works fine as long as postincrement increments count after evaluating it for its R-value. What's counterintuitive about the kvass' example is that it evaluates, increments, and then assigns. What I'd expect would be evaluate, assign, and then increment - which would work in your case and kvass' as well.

Hmmm - yes, you're right.

Yeah it was counter-intuitive for me as well -- I was expecting them to just turn in code with alphabet++ but I don't really see why alphabet = alphabet++ isn't incrementing at all. Does anyone have a concrete explanation or is this just a strange bizarre aspect of Java? xD these kinds of crazy code issues only surface in like Java 101... It takes a total noob to think them up ^^

Does anyone have a concrete explanation

You will find the detailed explanation of why it doesn't increment posted above, complete with links to the relevant part of the Java Language Ref. It's clearly an explicit design decision in the language and, is inherited verbatim from earlier languages - I found exactly the same details in my 1989 "Standard C" manual!
However, yes, since Java experts know to code i++, only a beginner would code i=i++ and confuse everybody :=)

Edited 5 Years Ago by JamesCherrill: n/a

Well, if you're not going to save time and write i=i++, instead of i++, chances are you'll write i=i+1;

I already posted an explanation from oracle's site, why this works the way it does. Deal with it!

But that's not the reason. i++ evaluates as i, so that is assigned to i, but then it should go ahead and increment i, so the net effect should be as the student expected.

Again, if
int b = a = 0;

then

b = a++;
System.out.printf("%d, %d", b, a);

gives
0, 1

so why is i not incremented, as a is?
I think the reason is down in the compiler, not in the spec.

I agree with Jon it seems counter-intuitive and the previous explanations posted isolate this case an exception to the general rule. The problem is likely in the compiler -- we were using BlueJ, but I wonder if this also happens on NetBeans and Eclipse -- anyone want to give it a try?

Test program:

public class incTest
{
	public static void main(String args[])
	{
		int a =7;
		int b =8;
		int c =9;

		a = b++;
		c = c++;


		System.out.printf("a = %d, b = %d, c = %d", a, b, c);
	}

}

output:
a = 8, b = 9, c = 9

jvm code, annotated:
(compiled w/o print statement for clarity)

public static void main(java.lang.String[]);
  Code:
   Stack=1, Locals=4, Args_size=1
   0:   bipush  7  // initialize 3 variables
   2:   istore_1
   3:   bipush  8
   5:   istore_2
   6:   bipush  9   
   8:   istore_3   //finish initializing
   9:   iload_2    // load value of variable #2 (b) onto the stack
   10:  iinc    2, 1   // increment variable #2
   13:  istore_1       // pop and store top of stack in #1 (a)
   14:  iload_3        // load #3 (c) onto the top of the stack
   15:  iinc    3, 1    // increment #3 (not top of stack - offset 3)
   18:  istore_3      // store value from top of stack to offset 3, wiping out previous value)
   19:  return

So this code loads the variable onto the stack, increments the variable (leaving what's on the stack untouched) and then stores what's on the stack back to where it's meant to go.
If you're doing something sensible like a = b++; ("sensible" being used loosely here) then it's going to do the right thing: load b onto the stack, increment whatever's at b, store what's at the top of the stack back to wherever it's going, which is a.
But if you're doing this clever thing c = c++; then you get:
load the value of c onto the stack, increment whatever's at c, load what's on the stack back to c, nuking the incremented value.

And this is actually a sensible way to do it, and it only produces a head-scratching result when you do something that you really shouldn't do.

But notice
a) it's not any more sensible than switching the store and the increment steps, which would produce the same result in the normal case and the expected result in the deviant case

and b) as far as I can see, this isn't defined behavior. A compiler could do it the other way, and be compliant with the spec. Unless I've missed something.


So, kvass, your student actually found something interesting. A tiny little chink in the monolithic java spec, which could conceivably cause code to run differently under different compilers.

Edited 5 Years Ago by jon.kiparsky: n/a

IMHO really don't see any ambiguity in the Lang Ref here. The whole of the RHS of an assignment has to be evaluated before the assignment to the LHS variable is made (unless the RHS terminates abnormally). The evaluation of the postfix operators is defined in terms of (1) their value and (2) the incrementing of the variable, so it's clear that both parts of the expression evaluation must be completed before the RHS value is assigned to the LHS variable.
If it worked the other way then the code would part-evaluate the RHS, perform the assignment to the LHS, then complete the evaluation of the RHS - clearly wrong.

@ztini

GCJ, a part of gcc which compiles C, Fortran, Pascal and other programming languages besides Java

VisualAge for Java was based on an extended Smalltalk ..

Jikes is a Java compiler that is supposedly faster than most other compilers

ECJ, the Eclipse Compiler for Java, is an open source incremental compiler used by the Eclipse JDT.

...

Need I go on?

This article has been dead for over six months. Start a new discussion instead.