Can (a==1 && a==2 && a==3) evaluate to true in Java?
We know it can in JavaScript.
But is it possible to print "Success" message on the condition given below in Java?
if (a==1 && a==2 && a==3) {
System.out.println("Success");
}
Someone suggested:
int _a = 1;
int a = 2;
int a_ = 3;
if (_a == 1 && a == 2 && a_ == 3) {
System.out.println("Success");
}
But by doing this we are changing the actual variable. Is there any other way?
Yes, it's quite easy to achieve this with multiple threads, if you declare variable a
as volatile.
One thread constantly changes variable a from 1 to 3, and another thread constantly tests that a == 1 && a == 2 && a == 3
. It happens often enough to have a continuous stream of "Success" printed on the console.
(Note if you add an else {System.out.println("Failure");}
clause, you'll see that the test fails far more often than it succeeds.)
In practice, it also works without declaring a
as volatile, but only 21 times on my MacBook. Without volatile
, the compiler or HotSpot is allowed to cache a
or replace the if
statement with if (false)
. Most likely, HotSpot kicks in after a while and compiles it to assembly instructions that do cache the value of a
. With volatile
, it keeps printing "Success" forever.
public class VolatileRace {
private volatile int a;
public void start() {
new Thread(this::test).start();
new Thread(this::change).start();
}
public void test() {
while (true) {
if (a == 1 && a == 2 && a == 3) {
System.out.println("Success");
}
}
}
public void change() {
while (true) {
for (int i = 1; i < 4; i++) {
a = i;
}
}
}
public static void main(String[] args) {
new VolatileRace().start();
}
}
Using concepts (and code) from a brilliant code golf answer, Integer
values can be messed with.
In this case, it can make int
s casted to Integer
s be equal when they wouldn't normally be:
import java.lang.reflect.Field;
public class Test
{
public static void main(String[] args) throws Exception
{
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
// array[129] is 1
array[130] = array[129]; // Set 2 to be 1
array[131] = array[129]; // Set 3 to be 1
Integer a = 1;
if(a == (Integer)1 && a == (Integer)2 && a == (Integer)3)
System.out.println("Success");
}
}
Unfortunately it's not quite as elegant as Erwin Bolwidt's multithreaded answer (as this one requires Integer
casting), but still some fun shenanigans take place.
In this question @aioobe suggests (and advise against) the use of C preprocessor for Java classes.
Although it is extremely cheaty, that's my solution:
#define a evil++
public class Main {
public static void main(String[] args) {
int evil = 1;
if (a==1 && a==2 && a==3)
System.out.println("Success");
}
}
If executed using the following commands it will output exactly one Success
:
cpp -P src/Main.java Main.java && javac Main.java && java Main
As we already know that it is possible to make this code evaluate to true thanks to great answers of Erwin Bolwidt and phflack, I wanted to show that you need to keep a high level of attention when dealing with a condition that looks like the one presented in the question, as sometimes what you see might not be exactly what you think it is.
This is my attempt to show that this code prints Success!
to the console. I know I cheated a bit, but I still think this is a good place to present it right here.
No matter what the purposes of writing code like this are - better to know how to deal with the following situation and how to check if you're not wrong with what you think you see.
I used the Cyrillic 'a' which is a distinct character from the latin 'a'. You can inspect the characters used in the if statement here.
This works because the names of the variables are taken from different alphabets. They are distinct identifiers, creating two distinct variables with a different value in each.
Note that if you want this code to work properly, character encoding needs to be changed to one supporting both characters, e.g. all Unicode encodings (UTF-8, UTF-16 (in BE or LE), UTF-32, even UTF-7), or Windows-1251, ISO 8859-5, KOI8-R (thank you - Thomas Weller and Paŭlo Ebermann - for pointing it out):
public class A {
public static void main(String[] args) {
int а = 0;
int a = 1;
if(а == 0 && a == 1) {
System.out.println("Success!");
}
}
}
(I hope you will never have to deal with that sort of problem any time in the future.)