Может (a == 1 && a == 2 && a == 3) оценивать true в Java?

Мы знаем, что это возможно в JavaScript.

Но возможно ли напечатать сообщение "Успех" при условии, приведенном ниже в Java?

if (a==1 && a==2 && a==3) {
    System.out.println("Success");
}

Кто-то предложил:

int _a = 1;
int a  = 2;
int a_ = 3;
if (_a == 1 && a == 2 && a_ == 3) {
    System.out.println("Success");
}

Но, делая это, мы меняем фактическую переменную. Есть ли другой способ?

Ответ 1

Да, довольно легко добиться этого несколькими потоками, если объявить переменную a как изменчивую.

Один поток постоянно меняет переменную a от 1 до 3, а другой поток постоянно проверяет, что a == 1 && a == 2 && a == 3. Это случается достаточно часто, чтобы на консоль был напечатан непрерывный поток "Успех".

(Обратите внимание, что если вы добавите предложение else {System.out.println("Failure");}, вы увидите, что тест выходит из строя намного чаще, чем это удается.)

На практике он также работает без объявления a как изменчивого, но только 21 раз на моем MacBook. Без volatile компилятору или HotSpot разрешено кэшировать a или заменить оператор if на if (false). Скорее всего, HotSpot запускается через некоторое время и компилирует его в инструкции сборки, которые кэшируют значение a. С volatile он сохраняет печать "Успех" навсегда.

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();
    }
}

Ответ 2

Используя понятия (и код) из блестящего ответа на гольф для гольфа, значения Integer могут быть испорчены.

В этом случае он может сделать равным int на Integer равным, если они обычно не будут:

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");
    }
}

К сожалению, это не так элегантно, как многопоточный ответ Erwin Bolwidt (поскольку для этого требуется кастинг Integer), но все же есть некоторые забавные махинации.

Ответ 3

В этот вопрос @aioobe предлагает (и советует) использовать препроцессор C для классов Java.

Несмотря на то, что cheaty чрезвычайно, что мое решение:

#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");
    }
}

Если выполняется с использованием следующих команд, он выдает ровно один Success:

cpp -P src/Main.java Main.java && javac Main.java && java Main

Ответ 4

Как мы уже знаем, возможно, чтобы этот код оценивался как истинный благодаря большим ответам Erwin Bolwidt и phflack, я хотел показать, что вам нужно уделять повышенное внимание при работе с условием, которое выглядит так, как оно представлено в вопросе, поскольку иногда то, что вы видите, может быть не таким, как вы думаете.

Это моя попытка показать, что этот код печатает Success! на консоли. Я знаю, что немного обманывал, но я все же думаю, что это хорошее место, чтобы представить его здесь.

Независимо от того, какие цели написания такого кода - лучше знать, как справиться со следующей ситуацией и как проверить, не ошибаетесь ли вы с тем, что, по вашему мнению, видите.

Я использовал кириллицу 'a', которая является отличным символом латинского 'a'. Вы можете проверить символы, используемые в выражении if здесь.

Это работает, потому что имена переменных берутся из разных алфавитов. Они представляют собой разные идентификаторы, создавая две разные переменные с различным значением в каждом.

Обратите внимание, что если вы хотите, чтобы этот код работал правильно, кодировка символов должна быть заменена на одну, поддерживающую оба символа, например. все кодировки Unicode (UTF-8, UTF-16 (в BE или LE), UTF-32, даже UTF-7) или Windows-1251, ISO 8859-5, KOI8-R (спасибо - Thomas Weller и Paŭlo Ebermann - для указания его):

public class A {
    public static void main(String[] args) {
        int а = 0;
        int a = 1;
        if(а == 0 && a == 1) {
            System.out.println("Success!");
        }
    }
}

(Надеюсь, вам никогда не придется иметь дело с такими проблемами в будущем).

Ответ 5

Есть еще один способ приблизиться к этому (в дополнение к неустойчивому подходу с использованием данных, который я опубликовал ранее), используя мощь PowerMock. PowerMock позволяет заменять методы другими реализациями. Когда это сочетается с автоматической распаковкой, исходное выражение (a == 1 && a == 2 && a == 3) без изменений может быть выполнено.

@phflack answer зависит от модификации процесса автоматического бокса в Java, который использует вызов Integer.valueOf(...). Нижеприведенный подход основан на изменении автоматической распаковки, изменив вызов Integer.intValue().

Преимущество нижеследующего подхода заключается в том, что исходный if-statement, заданный OP в вопросе, используется без изменений, что, по моему мнению, является самым элегантным.

import static org.powermock.api.support.membermodification.MemberMatcher.method;
import static org.powermock.api.support.membermodification.MemberModifier.replace;

import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@PrepareForTest(Integer.class)
@RunWith(PowerMockRunner.class)
public class Ais123 {
    @Before
    public void before() {
        // "value" is just a place to store an incrementing integer
        AtomicInteger value = new AtomicInteger(1);
        replace(method(Integer.class, "intValue"))
            .with((proxy, method, args) -> value.getAndIncrement());
    }

    @Test
    public void test() {
        Integer a = 1;

        if (a == 1 && a == 2 && a == 3) {
            System.out.println("Success");
        } else {
            Assert.fail("(a == 1 && a == 2 && a == 3) != true, a = " + a.intValue());
        }
    }

}

Ответ 6

Поскольку это, кажется, продолжение этого вопроса JavaScript, стоит отметить, что этот трюк и аналогичные работы на Java тоже:

public class Q48383521 {
    public static void main(String[] args) {
        int aᅠ = 1;
        int ᅠ2 = 3;
        int a = 3;
        if(aᅠ==1 && a==ᅠ2 && a==3) {
            System.out.println("success");
        }
    }
}

В Ideone


Но обратите внимание, что это не самое худшее, что вы могли бы сделать с Unicode. Использование белых или управляющих символов, которые являются действительными идентификационными частями или с использованием разных букв, которые выглядят одинаково, все еще создает идентификаторы, которые являются разными и могут быть замечены, например. при выполнении текстового поиска.

Но эта программа

public class Q48383521 {
    public static void main(String[] args) {
        int ä = 1;
        int ä = 2;
        if(ä == 1 && ä == 2) {
            System.out.println("success");
        }
    }
}

использует два идентификатора, которые являются одинаковыми, по крайней мере, с точки зрения Unicode. Они просто используют разные способы кодирования одного и того же символа ä, используя U+00E4 и U+0061 U+0308.

On Ideone

Таким образом, в зависимости от используемого вами инструмента они могут не только выглядеть одинаково, но и текстовые инструменты с поддержкой Unicode могут даже не сообщать о каких-либо различиях, всегда находя их при поиске. Возможно, у вас может возникнуть проблема с тем, что разные представления теряются при копировании исходного кода кому-то другому, возможно, попытка получить помощь для "странного поведения", что делает его невоспроизводимым для помощника.

Ответ 7

Вдохновленный @Erwin отлично answer, я решил доказать, что это возможно с использованием Java Stream API (параллельные потоки, чтобы быть точным). Технически подход такой же, просто различная интерпретация.

Интересно, что мое решение работает, но в очень редких случаях (компилятор точно в срок может оптимизировать код).

Хитрость заключается в отключении любых оптимизаций JIT с использованием следующей опции VM:

-Djava.compiler=NONE

В этой ситуации число успешных случаев значительно увеличивается! Вот мой код:

class Race {
    private static int a;

    public static void main(String[] args) {
        IntStream.range(0, 100_000).parallel().forEach(i -> {
            a = 1;
            a = 2;
            a = 3;
            testValue();
        });
    }

    private static void testValue() {
        if (a == 1 && a == 2 && a == 3) {
            System.out.println("Success");
        }
    }
}

Параллельный поток использует ForkJoinPool под капотом, поэтому переменная a делится между несколькими потоками без каких-либо синхронизация, поэтому результат не детерминирован.

Ответ 8

Вдоль похожих строк, заставляя поплавок (или двойной) переполнять (или переполнять) путем деления (или умножения) на большое число:

int a = 1;
if (a / Float.POSITIVE_INFINITY == 1 / Float.POSITIVE_INFINITY
        && a / Float.POSITIVE_INFINITY == 2 / Float.POSITIVE_INFINITY
        && a / Float.POSITIVE_INFINITY == 3 / Float.POSITIVE_INFINITY) {
    System.out.println("Success");
}