Java에서 (a == 1 && a == 2 && a == 3)을 true로 평가할 수 있습니까?


176

우리는 그것이 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");
}

그러나 이렇게하면 실제 변수가 변경됩니다. 다른 방법이 있습니까?


5
&&논리 and연산자 a이므로 논리적으로 불가능한 값 1, 2 및 3을 동시에 가져야합니다. 대답은 아니요, 불가능합니다. 1, 2 또는 3 값 중 하나 if가 있는지 확인 하는 명령문 을 작성 a하시겠습니까?
보리스 파블로 비치

16
참고 사항 :이 질문은 Javascript와 동일한 질문 인 stackoverflow.com/questions/48270127/… 에서 비롯 될 수 있습니다.이 경우 대답은yes
nos

28
@ArthurAttout 중복이 아닙니다. 그 질문은 Java가 아니라 Javascript에 관한 것입니다.
nos

13
변수 이름에 공백을 사용하는 것은 Java에서도 작동합니다. 그러나 물론 이것은 기본적으로를 사용하는 것과 동일하지만 _볼 수는 없습니다.
tobias_k

8
@Seelenvirtuose True이지만 if(a==1 && a==2 && a==3)반드시 동시에 평가 될 필요는 없습니다. 그리고 이것은 유니 코드 트릭에 의지하지 않고도이 작업을 수행하는 데 사용할 수 있습니다.
Erwin Bolwidt

답변:


325

예, 변수 a를 휘발성으로 선언하면 여러 스레드 로이 작업을 수행하는 것이 매우 쉽습니다 .

하나의 스레드는 변수 a를 1에서 3으로 지속적으로 변경하고 다른 스레드는 지속적으로 테스트합니다 a == 1 && a == 2 && a == 3. 콘솔에 연속적인 "성공"스트림을 인쇄하기에 충분한 경우가 종종 있습니다.

( else {System.out.println("Failure");}구문 을 추가 하면 테스트가 성공보다 훨씬 자주 실패한다는 것을 알 수 있습니다.)

실제로, 그것은 a휘발성으로 선언하지 않고 MacBook에서 21 번만 작동합니다 . 이 없으면 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();
    }
}

83
이것은 훌륭한 예입니다! 다음 번에 주니어 개발자와 잠재적으로 위험한 동시성 문제를 논의 할 때 그것을 훔칠 것이라고 생각합니다. :)
Alex Pruss

6
키워드 가 없으면 가능성이 높지 volatile않지만 원칙적으로 volatile키워드 없이도 발생할 수 있으며 임의의 횟수로 발생할 수 있지만 volatile. 그러나 실제로 그것이 실제로 일어나고 있다는 것은 인상적입니다…
Holger

5
@AlexPruss 난 그냥 한 모든 DEVS에, 내 회사에서뿐 아니라 후배는 (내가 알고 답변에서 실패의 87 % 성공 가능 수)
유진

3
@Holger True, 어느 것도 보증하지 않습니다. 두 스레드가 별도의 코어에 할당되는 일반적인 다중 풀 코어 또는 멀티 CPU 아키텍처에서는 발생할 수 있습니다 volatile. 구현 된 메모리 장벽 volatile은 스레드 속도를 늦추고 짧은 시간 동안 비동기식으로 작동 할 가능성을 높입니다. 예상보다 훨씬 자주 발생합니다. 타이밍에 매우 민감하지만, (a == 1 && a == 2 && a == 3)수익률 평가의 0.2 % ~ 0.8 % 정도입니다 true.
Erwin Bolwidt

4
이것은 실제로 작은 변형을 만드는 대신 의도 된 코드가 실제로 반환되도록하는 유일한 대답입니다. +1
Alejandro

85

A로부터 개념 (코드)를 사용하여 화려한 코드 골프 대답을 , Integer값이 엉망이 될 수있다.

이 경우 일반적으로 그렇지 않을 때 ints로 캐스팅 된 Integers를 동일하게 만들 수 있습니다.

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주물 이 필요하기 때문에)만큼 우아 하지는 않지만 여전히 재미있는 재미가 있습니다.


매우 흥미로운. 나는 학대와 함께 놀았다 Integer. 캐스팅에 대한 수치심이지만 여전히 시원합니다.
Michael

@ 마이클 나는 캐스팅을 제거하는 방법을 알 수 없습니다. a1/2/3으로 설정하면 만족 a == 1하지만, 다른 방향으로는 가지 않습니다
phflack

4
며칠 전에 JS / Ruby / Python and에서이 질문에 답변했습니다 Java. 내가 찾을 수있는 최소한 추한 버전을 사용하는 것이 었습니다 a.equals(1) && a.equals(2) && a.equals(3),하는 힘 1, 2그리고 3로 오토 박싱 수 Integer의.
Eric Duminil

@EricDuminil 당신이 a수업을 한다면 boolean equals(int i){return true;}어떻게 될까요?
phflack

1
내가 말했듯이, 그것은 가장 추악하지만 여전히 최적은 아닙니다. 으로 Integer a = 1;전 라인에, 그것은 분명 아직 a정말 정수입니다.
Eric Duminil

52

에서 이 질문 @aioobe는 (에 대한 및 조언) 자바 클래스 C 프리 프로세서의 사용을 제안한다.

그것이 매우 쾌적 하지만 그것은 나의 해결책입니다.

#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

6
검색 (find)를 통해 코드를 실행하고 교체하기 전에 실제로 그것을 컴파일하지만, 나는 확실히 워크 플로우의 한 부분으로 그런 일을하는 사람을 볼 수 있었다 같은 느낌의이 종류
phflack

1
@phflack 나는 그것을 좋아한다. 틀림없이이 질문은 코드를 읽을 때 가정을 할 때의 위험을 보여주는 것에 관한 것이다. a변수 라고 가정하는 것은 쉽지만 전처리기를 사용하는 언어의 임의의 코드 조각이 될 수 있습니다.
kevin

2
자바가 아닙니다. "C 전처리기를 통과 한 후의 Java"언어입니다. 답변에 사용 된 비슷한 허점 . (참고 : 코드 골프에서는 언더 핸드주제벗어
났습니다

40

우리가 이미 알고 있듯이 것이 가능 의 큰 응답에 대한 진정한 감사로 평가이 코드를 만들기 위해 어윈 Bolwidtphflack을 것과 같은 모양이되게하는 것이, 나는 조건을 처리 할 때주의의 높은 수준을 유지해야한다는 것을 보여주고 싶었다 때로는 당신이 보는 것이 정확히 당신이 생각하는 것이 아닐 수도 있습니다.

이것은이 코드 Success!가 콘솔에 인쇄된다는 것을 보여주기위한 나의 시도 입니다. 나는 속임수를 쓴다는 것을 알고 있지만 여전히 여기에 그것을 제시하기에 좋은 곳이라고 생각합니다.

이와 같은 코드 작성의 목적이 무엇이든 관계없이 다음 상황을 처리하는 방법과 자신이 생각하는 내용이 잘못되었는지 확인하는 방법을 아는 것이 좋습니다.

나는 라틴어 'a'와는 구별되는 키릴 문자 'a'를 사용했습니다. 여기서 if 문에 사용 된 문자를 검사 할 수 있습니다 .

변수 이름이 다른 알파벳에서 가져 오기 때문에 작동합니다. 그것들은 별개의 식별자이며 각각 다른 값을 가진 두 개의 별개의 변수를 만듭니다.

이 코드가 제대로 작동하려면 문자 인코딩을 두 문자를 모두 지원하는 문자로 변경해야합니다 (예 : 모든 유니 코드 인코딩 (UTF-8, UTF-16 (BE 또는 LE)), UTF-32, 심지어 UTF-7) ) 또는 Windows-1251, ISO 8859-5, KOI8-R ( Thomas WellerPa Elo Ebermann- 감사합니다 ) :

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

(향후 언제라도 이런 종류의 문제를 다루지 않아도되기를 바랍니다.)


@Michael 잘 보이지 않는다는 것은 무엇을 의미합니까? 나는 그것을 휴대 전화에 썼고 여기에서 데스크탑보기로 전환 할 때도 괜찮아 보입니다. 그것이 내 대답을 더 좋게 만들려면 지금 조금 어려워서 명확하게 편집하십시오.
Przemysław Moskal

@Michael 대단히 감사합니다. 나는 나의 대답의 끝에서 인정하는 것이 충분하다고 생각했다. :)
Przemysław Moskal

@mohsenmadi 그래, 나는 그것을 휴대 전화에서 확인할 수는 없지만 편집하기 전에 마지막 문장이 내 예제에서 다른 언어를 사용하는 것에 대해 확신합니다 : P
Przemysław Moskal

█ == 0이 a == 1과 비슷한 이유는 무엇입니까?
Thomas Weller

1
더 많은 nitpicking : UTF-8 (어쨌든 권장되지만), 편집기에 인코딩이 무엇인지 (및 컴파일러도) 알려주 는 한 둘 다 가지고 а있고 a작동 하는 문자 인코딩은 반드시 필요하지는 않습니다 . 모든 유니 코드 인코딩 (UTF-8, UTF-16 (BE 또는 LE), UTF-32, 심지어 UTF-7)은 물론 Windows-1251, ISO 8859-5, KOI8-R과 같이 작동합니다.
Paŭlo Ebermann

27

PowerMock의 힘을 사용하여이 방법에 접근 할 수있는 다른 방법이 있습니다 (앞서 게시 한 휘발성 데이터 레이싱 접근 방식에 추가). PowerMock을 사용하면 메소드를 다른 구현으로 대체 할 수 있습니다. 그것이 자동 언 박싱과 결합 될 때, 원래의 표현(a == 1 && a == 2 && a == 3) 은 수정없이 이루어질 수 있습니다.

@phflack의 답변은 Integer.valueOf(...)호출 을 사용하는 Java의 자동 복싱 프로세스 수정에 의존합니다 . 아래 접근 방식은 Integer.intValue()통화 를 변경하여 자동 우편함을 수정하는 방법에 의존합니다 .

아래 접근법의 장점은 문제의 OP에서 제공 한 원래 if 문이 변경되지 않고 사용된다는 것입니다.

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

}

Powermock 은 내부 / 외부 클래스의 필드 access$nnn를 읽는 데 사용되는 방법과 같은 합성 방법을 대체 할 수 있습니까 private? 그것은 다른 흥미로운 변형을 허용 할 것입니다 ( int변수로 도 작동 함 )…
Holger

@Holger 흥미로운 아이디어, 무슨 말인지 알겠습니다. 아직 작동하지 않습니다-작동을 멈추는 것이 무엇인지 명확하지 않습니다.
Erwin Bolwidt

정말 좋았습니다. 그렇지만 바이트 코드를 조작하지 않으면 불변 클래스에서 정적 메서드를 변경하는 것이 매우 어려울 수 있습니다. 내가 틀린 것 같지만 PowerMock에 대해 들어 본 적이 없다면 어떻게되는지 궁금합니다. 부수적으로, phflack의 대답은 오토 박스에 의존하지 않습니다 : 그는 캐시 된 정수의 주소를 변경합니다 (따라서 ==는 실제로 값이 아닌 Integer 객체 주소를 비교합니다).
Asoub

2
@ 캐스팅 ( (Integer)2) 상자에 int를 넣습니다 . 리플렉션을 자세히 살펴보면 리플렉션을 사용하여 언 박싱을 사용하여이 작업을 수행 할 수없는 것처럼 보이지만 대신 계측 (또는이 답변에서 PowerMock을 사용하여)으로
phflack

@Holger 아마도 대체 access$0방법 을 등록 할 수 있지만 등록시 메소드의 존재를 확인 하지만 합성 방법을 가로 채지 않을 것입니다 . 그러나 교체는 호출되지 않습니다.
Erwin Bolwidt

17

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

이데온에서


그러나 이것이 유니 코드로 할 수있는 최악의 일은 아닙니다. 유효한 식별자 부분 인 공백이나 제어 문자를 사용 하거나 같은 모양의 다른 문자를 사용 하면 텍스트 검색을 수행 할 때와 같이 다르게 식별 될 수있는 식별자가 생성됩니다.

하지만이 프로그램은

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

적어도 유니 코드 관점에서 볼 때 동일한 두 개의 식별자를 사용합니다. 그들은 and를 ä사용하여 동일한 문자를 인코딩하는 다른 방법을 사용합니다.U+00E4U+0061 U+0308 .

이데온에서

따라서 사용하는 도구에 따라 동일하게 보일뿐만 아니라 유니 코드 가능 텍스트 도구도 차이를보고하지 않을 수 있으며 검색시 항상 두 가지를 모두 찾을 수 있습니다. 소스 코드를 다른 사람에게 복사 할 때 다른 표현이 손실되는 문제가있을 수 있습니다. 아마도 "이상한 동작"에 대한 도움말을 얻으려고 시도하면 도우미가 재현 할 수 없게됩니다.


3
예, 이 답변 은 유니 코드 남용을 충분히 다루지 않습니까?
Michael

2
int ᅠ2 = 3;의도적인가요? 왜냐하면, 나는 아주 이상한 코드를 보았습니다
Ravi

1
@Michael은 정확히 그 답이 두 가지 다른 문자 (검색 할 때 IDE가 다르게 생각할 것임 a)를 사용하는 것에 관한 것이지만 공백에 관한 것이므로 관련 JavaScript 질문에 대한 더 오래된 답변을 인정합니다. 내 의견 은 심지어 자바 질문보다
Holger

2
나는 구별이 보이지 않습니다. 두 답변 모두 if 문의 3 가지 식별자가 시각적으로 유사하지만 특이한 유니 코드 문자를 사용함으로써 기술적으로 구별되는 솔루션을 제공합니다. 공백인지 키릴 문자인지 여부는 관련이 없습니다.
Michael

2
@Michael 당신은 그렇게 볼 수 있습니다, 그것은 당신에게 달려 있습니다. 말했듯이, 5 일 전에 JavaScript에 대한 답변은 질문의 역사를 고려하여 Java에도 적용된다고 말하고 싶습니다. 예, 이는 연결된 JavaScript 질문을 나타내지 않는 다른 답변에 다소 중복됩니다. 어쨌든, 나는 시각적으로 비슷한 문자가 아니라 동일한 문자 를 인코딩 하는 다른 방법을 나타내는 사례를 추가하기 위해 답변을 업데이트 했습니다. 심지어 "유일한 유니 코드 문자"도 아닙니다. 매일 사용하는 캐릭터입니다…
Holger

4

@Erwin의 탁월한 답변 에서 영감을 얻은 비슷한 예제를 작성했지만 Java Stream API를 사용했습니다 .

흥미로운 점은 내 솔루션이 작동하지만 매우 드문 경우입니다 ( just-in-time컴파일러가 그러한 코드를 최적화 하기 때문에   ).

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

PS 병렬 스트림 ForkJoinPool은 후드에서 사용되며 변수 a 는 동기화없이 여러 스레드간에 공유되므로 결과가 결정적이지 않습니다.


1

유사한 행을 따라 float (또는 double)을 큰 수로 나누기 (또는 곱셈)를 통해 언더 플로우 (또는 오버플로)로 강제합니다.

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

2
@PatrickRoberts-이 특정 동작은 Java 문서에 따라 부동 소수점 언더 플로 입니다. 또한 볼 , , & .
Aloke
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.