Java'da (a == 1 && a == 2 && a == 3) doğru olarak değerlendirilebilir mi?


176

JavaScript ile yapabileceğini biliyoruz .

Ancak, Java'da aşağıda verilen koşula "Başarılı" mesajını yazdırmak mümkün müdür?

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

Birisi önerdi:

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

Ancak bunu yaparak gerçek değişkeni değiştiriyoruz. Başka yolu var mı?


5
&&mantıksal andoperatördür, yani amantıksal olarak imkansız olan aynı zamanda 1, 2 ve 3 değerine sahip olmalıdır. Cevap HAYIR, mümkün değil. 1, 2 VEYA 3 değerlerinden birine sahip olup ifolmadığını kontrol eden bir ifade yazmak ister misiniz a?
Boris Pavlović

16
Herkese not: Bu soru Javascript için aynı sorudan gelebilir: stackoverflow.com/questions/48270127/… , bu durumda cevapyes
nos

28
@ArthurAttout Yinelenen değil, bu soru Java için değil Javascript içindir.
nos

13
Değişken adlarında boşluk kullanan Java da çalışır. Ama tabii ki bu temel olarak kullanmakla aynı _, ancak göremiyorsunuz.
tobias_k

8
@Seelenvirtuose Doğru, ancak if(a==1 && a==2 && a==3)aynı zamanda mutlaka değerlendirilmesi gerekmez. Ve bu, Unicode numaralarına başvurmadan bu işi yapmak için kullanılabilir.
Erwin Bolwidt

Yanıtlar:


325

Evet, değişkeni ageçici olarak bildirirseniz , bunu birden çok iş parçacığıyla başarmak oldukça kolaydır .

Bir iş parçacığı sürekli olarak a değişkenini 1'den 3'e değiştirir ve başka bir iş parçacığı bunu sürekli olarak test eder a == 1 && a == 2 && a == 3. Konsolda sürekli bir "Başarı" akışı basılması yeterli olur.

(Bir else {System.out.println("Failure");}cümle eklerseniz , testin başarılı olduğundan çok daha sık başarısız olduğunu göreceksiniz.)

Pratikte, auçucu olarak bildirmeden de çalışır , ancak MacBook'umda sadece 21 kez. Olmadan volatile, derleyicinin veya HotSpot'un ifadeyi önbelleğe almasına aveya değiştirmesine izin verilir . Büyük olasılıkla, HotSpot bir süre sonra devreye girer ve değerini önbelleğe alan montaj talimatlarına derler . İle , "Başarı" sonsuza kadar yazdırmaya devam ediyor.ifif (false)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
Bu harika bir örnek! Sanırım bir dahaki sefere bir genç geliştirici ile potansiyel olarak tehlikeli eşzamanlılık meselelerini
tartışacağım.

6
Olması muhtemel değildir volatile, ancak prensip olarak, bu volatileanahtar kelime olmadan bile olabilir ve keyfi olarak birkaç kez olabilir, yandan da, bununla birlikte olacağına dair bir garanti yoktur volatile. Ama elbette, pratikte olması, oldukça etkileyici…
Holger

5
@AlexPruss Ben sadece tüm devs üzerinde yaptım, sadece şirketimdeki gençler değil ( mümkün olabileceğini biliyordum ), cevaplarda başarısızlıkların% 87 başarısı
Eugene

3
@Holger True, ikisinin de garantisi yok. Her iki iş parçacığının ayrı bir çekirdeğe atandığı tipik bir çoklu tam çekirdekli veya çoklu işlemci mimarisinde, bunun olması muhtemeldir volatile. Uygulamak için oluşturulan bellek bariyerleri volatileiş parçacıklarını yavaşlatır ve senkronize olmalarının kısa süreler boyunca daha fazla yapılmasını sağlar. Beklediğimden çok daha sık oluyor. Çok zamanlamaya duyarlı, ancak (a == 1 && a == 2 && a == 3)geri dönüş değerlendirmelerinin yaklaşık% 0,2 ila% 0,8'i olduğunu görüyorum true.
Erwin Bolwidt

4
Bu aslında sadece küçük değişimler yapmak yerine amaçlanan kodun doğru dönmesini sağlayan tek cevaptır. +1
Alejandro

85

Parlak bir kod golf cevap kavramları (ve kodu) kullanarak , Integerdeğerler berbat edilebilir.

Bu durumda, yapabilir intler Dökümdür için Integernormalde olmaz ne zaman s eşit:

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

Ne yazık ki, Erwin Bolwidt'ün çok iş parçacıklı yanıtı (bu, Integerdöküm gerektirdiği için ) kadar zarif değil , ama yine de bazı eğlenceli maskaralıklar gerçekleşiyor.


Çok ilginç. İstismarla oynuyordum Integer. Döküm hakkında bir utanç ama yine de harika.
Michael

@Michael Dökümden nasıl kurtulacağımı tam olarak anlayamıyorum. a1/2/3 olarak ayarlanması tatmin edici olacaktır a == 1, ancak diğer yöne gitmez
phflack

4
Bu soruyu birkaç gün önce JS / Ruby / Python ve Java. Ben bulabildiğim en çirkin versiyonu kullanmaktı a.equals(1) && a.equals(2) && a.equals(3), hangi güçler 1, 2ve 3olarak autoboxed edilecek Integers.
Eric Duminil

@EricDuminil Bu biraz eğlenebilir, çünkü ya abir sınıf olsaydınız boolean equals(int i){return true;}?
phflack

1
Dediğim gibi, en az çirkin ama yine de optimal değil. Daha Integer a = 1;önce hatta olan, hala abir Tamsayı olduğu açıktır .
Eric Duminil

52

Gelen bu soru @aioobe (karşı ve tavsiye) Java sınıfları için C önişlemcisine kullanılmasını önermektedir.

Son derece ucuz olmasına rağmen , bu benim çözümüm:

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

Aşağıdaki komutlar kullanılarak yürütülürse, tam olarak bir komut verilir Success:

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

6
Bu tür, aslında derlemeden önce bir bul ve değiştir yoluyla kod çalıştırmak gibi hissediyor, ancak kesinlikle birisinin iş akışının bir parçası olarak böyle bir şey yaptığını görebiliyordum
phflack

1
@phflack Sevdim, tartışmasız bu soru kod okurken varsayımlarda bulunmanın tehlikelerini göstermekle ilgilidir. aBir değişken olduğunu varsaymak kolaydır , ancak önişlemcili dillerde rastgele herhangi bir kod parçası olabilir.
kevin

2
Java değil. "C ön işlemcisinden geçtikten sonra Java" disidir. Bu cevap tarafından kullanılan benzer boşluk . : (not hileli olduğu -konu dışı Kod Golf için şimdi)
user202729

40

Bunu zaten bildiği gibi mümkün büyük cevaplara gerçek sayesinde değerlendirmek bu kodu yapmak için Erwin Bolwidt ve phflack gibi görünüyor sunulan, ben bir koşulu ile uğraşırken sen dikkat yüksek düzeyde tutmak gerektiğini göstermek istedik soruda, bazen gördüğünüz şey tam olarak düşündüğünüz şey olmayabilir.

Bu, bu kodun Success!konsola yazdırıldığını gösterme girişimim . Biraz hile yaptığımı biliyorum , ama hala burada sunmak için iyi bir yer olduğunu düşünüyorum.

Bunun gibi kod yazmanın amaçları ne olursa olsun - aşağıdaki durumla nasıl başa çıkılacağını ve gördüğünüzü düşündüğünüzde yanlış olup olmadığınızı nasıl kontrol edeceğinizi bilmek daha iyi.

Latince 'a' dan farklı bir karakter olan Kiril 'a' kullandım. Burada if ifadesinde kullanılan karakterleri inceleyebilirsiniz .

Bu, değişkenlerin isimleri farklı alfabelerden alındığı için çalışır. Her biri farklı bir değere sahip iki farklı değişken yaratan farklı tanımlayıcılardır.

Bu kodun düzgün çalışmasını istiyorsanız, karakter kodlamanın her iki karakteri de destekleyen bir karakterle değiştirilmesi gerekir, örneğin tüm Unicode kodlamaları (UTF-8, UTF-16 (BE veya LE'de), UTF-32, hatta UTF-7 ) veya Windows-1251, ISO 8859-5, KOI8-R ( işaret ettiğiniz için teşekkür ederiz - Thomas Weller ve 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!");
        }
    }
}

(Umarım gelecekte bu tür bir sorunla asla uğraşmak zorunda kalmazsınız.)


@Michael Ne demek iyi görünmüyor? Cep telefonuna yazdım ve burada masaüstü görünümüne geçerken bile bana iyi görünüyor. Cevabımı daha iyi yaparsa, şimdi biraz zor bulduğumdan açıkça belirtin.
Przemysław Moskal

@Michael Çok teşekkür ederim. Cevabımın sonunda kabul ettiğim şeyin yeterli olduğunu düşündüm :)
Przemysław Moskal

@mohsenmadi Evet, burada cep telefonundan kontrol edemiyorum, ancak düzenlemeden önce son cümlenin örneğimdeki farklı dilleri kullanmakla ilgili olduğundan eminim: P
Przemysław Moskal

█ == 0 neden == 1 ile benzer olmalı?
Thomas Weller

1
Daha kusur arama: (Bu durumda önerilir rağmen) Mutlaka, hem sahip herhangi bir karakter kodlamasını UTF-8 gerekmez аve asürece (çok ve muhtemelen derleyici) içinde bulunduğu kodlayan ne editörü anlatmak gibi, eserlerini. Tüm Unicode kodlamaları (UTF-8, UTF-16 (BE veya LE'de), UTF-32, hatta UTF-7) ve ayrıca Windows-1251, ISO 8859-5, KOI8-R gibi çalışır.
Paŭlo Ebermann

27

PowerMock'un gücünü kullanarak (daha önce yayınladığım değişken veri yarışları yaklaşımına ek olarak) buna yaklaşmanın başka bir yolu var. PowerMock, yöntemlerin diğer uygulamalarla değiştirilmesine izin verir. Bu, otomatik (a == 1 && a == 2 && a == 3)kutu açma ile birleştirildiğinde, orijinal ifade , değişiklik yapılmadan gerçekleştirilebilir.

@ phflack'ın yanıtı, Java'da Integer.valueOf(...)çağrıyı kullanan otomatik boks işlemini değiştirmeye dayanır . Aşağıdaki yaklaşım, Integer.intValue()çağrıyı değiştirerek otomatik kutulamayı değiştirmeye dayanmaktadır .

Aşağıdaki yaklaşımın avantajı, OP tarafından soruda verilen orijinal if ifadesinin değişmeden kullanılmasıdır ki bence en zarif olanıdır.

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 , iç / dış sınıf alanlarını access$nnnokumak için kullanılan yöntemler gibi sentetik yöntemlerin yerini alabilir privatemi? Bu, bazı ilginç varyantlara izin verir (bir intdeğişkenle bile çalışır )…
Holger

@Holger İlginç bir fikir, ne demek istediğini anlıyorum. Henüz çalışmıyor - çalışmasını engelleyen şeyin ne olduğu belli değil.
Erwin Bolwidt

Gerçekten güzel, bunu yapmaya çalışıyordum, ama değişmez sınıftan statik yöntemi değiştirmenin bayt kodunu değiştirmeden oldukça zor olacağını buldum. Yanılmışım gibi görünüyor, ama PowerMock'u hiç duymadım, bunu nasıl yaptığını merak ediyorum. Bir yan not olarak, phflack'ın yanıtı otomatik kutulamaya dayanmaz: önbelleğe alınmış Tamsayının adreslerini değiştirir (yani == aslında değerler yerine Tamsayı nesne adreslerini karşılaştırır).
Asoub

2
@ Döküm ( (Integer)2) kutularını int. Daha fazla düşünmeye baktığımızda , bunu yansıma kullanarak kutudan çıkarma ile yapmak mümkün görünmüyor, ancak bunun yerine Enstrümantasyon ile (veya bu cevaptaki gibi PowerMock ile)
phflack

@Holger muhtemelen sentetik bir yöntemi engellemiyor, ancak yerine bir yedek kaydetmeme izin veriyor access$0ve bir yöntemin varlığı kayıt sırasında kontrol ediliyor . Ancak değiştirme işlemi asla başlatılmaz.
Erwin Bolwidt

17

Bu, bu JavaScript sorununun bir takibi gibi göründüğünden , bu hile ve benzerlerinin Java'da da çalıştığını belirtmek gerekir :

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'da


Ancak bunun Unicode ile yapabileceğiniz en kötü şey olmadığını unutmayın. Geçerli tanımlayıcı parçalar olan boşluk veya kontrol karakterleri kullanma veya kullanmak aynı görünen farklı harfler kullanmak, yine de farklı ve örneğin metin araması yaparken tespit edilebilen tanımlayıcılar oluşturur.

Ama bu program

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

en azından Unicode bakış açısından aynı olan iki tanımlayıcı kullanır. Sadece aynı karakteri kodlamak için farklı yollar äkullanırlar.U+00E4 ve U+0061 U+0308.

Ideone'da

Bu nedenle, kullandığınız araca bağlı olarak, yalnızca aynı görünmeyebilirler, Unicode özellikli metin araçları herhangi bir fark bildirmeyebilir, her zaman arama yaparken her ikisini de bulur. Kaynak kodu bir başkasına kopyalarken farklı temsillerin kaybolması, belki de “garip davranış” için yardım almaya çalışılması ve hatta yardımcı için tekrarlanamaz hale getirilmesi sorunu bile yaşayabilirsiniz.


3
Eh, bu cevap unicode kötüye kullanımı yeterince kapsamıyor mu?
Michael

2
int ᅠ2 = 3;kasıtlı mı? Çünkü her yerde çok garip bir kod görüyorum
Ravi

1
@Michael tam olarak değil, çünkü bu cevap iki farklı harf kullanmakla ilgilidir (hangi IDE'ler ararken farklı düşünürler a), oysa bu boşlukla ilgilidir ve ilgili JavaScript sorusundaki daha eski cevaplara kredi verir. O Not orada benim yorum (birkaç gün) Java soruya daha yaşlı ...
Holger

2
Ayrımı görmüyorum. Her iki yanıt da if ifadesindeki üç tanımlayıcının görsel olarak benzer ancak olağandışı unicode karakterler kullanması nedeniyle teknik olarak farklı olduğu bir çözüm sunmaktadır. İster boşluk ister Kiril olsun, önemsizdir.
Michael

2
@ Michael bunu bu şekilde görebilirsiniz, bu size kalmış. Dediğim gibi, beş gün önce JavaScript için verilen cevabın sadece sorunun tarihi göz önünde bulundurularak Java için de geçerli olduğunu belirtmek istedim. Evet, bu, bağlantılı JavaScript sorusuna atıfta bulunmayan başka bir cevap için biraz gereksiz. Her neyse, bu arada cevabımı görsel olarak benzer karakterler hakkında değil , aynı karakteri kodlamanın farklı yolları olan bir vaka eklemek için güncelledim ve hatta “alışılmadık bir unicode karakter” değil; her gün kullandığım bir karakter…
Holger

4

@ Erwin'in mükemmel cevabından esinlenerek benzer bir örnek yazdım, ancak Java Stream API kullanarak .

Ve ilginç bir şey, çözümümün çalışması, ancak çok nadir durumlarda (   just-in-timederleyici böyle bir kodu optimize ettiği için).

İşin püf noktası JIT, aşağıdaki VMseçeneği kullanarak tüm optimizasyonları devre dışı bırakmaktır :

-Djava.compiler=NONE

Bu durumda, başarı vakalarının sayısı önemli ölçüde artar. İşte kod:

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 Paralel akışlar ForkJoinPoolkaputun altında kullanılır ve değişken a , herhangi bir senkronizasyon olmadan birden çok iş parçacığı arasında paylaşılır, bu nedenle sonuç deterministik değildir.


1

Benzer hatlar boyunca , bir şamandırayı (veya çiftli) bölünme (veya çarpma) yoluyla çok sayıda sayı ile alt-akışa (veya taşmaya) zorlayarak:

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 - bu özel davranış, Java belgelerine göre kayan nokta taşmasıdır . Ayrıca bakınız bu , bu , bu ve bu .
Aloke
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.