2015-07-30 21 views
8

Przepraszam, jeśli to jest pytanie dla początkujących, ale nie mogłem znaleźć odpowiedzi na to. Czy lepiej jest to zrobić:Zmienne lokalne przed zwrotnymi instrukcjami, czy to ma znaczenie?

int result = number/number2; 
return result; 

czyli

return number/number2; 

wiem całkowite wykorzystanie pamięci więc zgaduję będzie nieznacznie zmniejszyć wydajność? Ale z drugiej strony sprawia, że ​​rzeczy są wyraźniejsze, szczególnie gdy int/string jest długim obliczeniem.

Dzięki za wszelkie dane wejściowe!

+6

Jeśli naprawdę nie próbujesz zoptymalizować wydajności, zawsze należy nadać priorytet czytelności. – Gnarlywhale

+1

Zgaduję, że nie użyłbyś pomocy pomocniczej z czymś tak prostym, jak numer/numer2? – SJ19

+1

Deklarowanie zmiennej lokalnej pomaga w debugowaniu, gdy chcesz sprawdzić wartość zwracaną przed opuszczeniem funkcji. – HDave

Odpowiedz

13

Istnieje w rzeczywistości zasada SonarQube odziedziczona z PMD o nazwie Unnecessary Local Before Return, która mówi o tym. Jest napisane:

Unikaj niepotrzebnego tworzenia zmiennych lokalnych.

Zasada ta była later replaced przez SSLR reguły Variables should not be declared and then immediately returned or thrown, który utrzymuje tę samą pozycję:

deklarowanie zmiennej tylko do niezwłocznego zwrotu lub rzucać to złe praktyka. Niektórzy programiści twierdzą, że praktyka poprawia czytelność kodu , ponieważ umożliwia im jawne nadanie nazwy zwracanej wartości. Jednak zmienna ta jest wewnętrznym szczegółem implementacji , który nie jest narażony na wywołania metody. Nazwa metody powinna być wystarczająca dla osób dzwoniących, aby dokładnie wiedzieć, co będzie zwrócił.

I całkowicie się z tym zgadzam.

IntelliJ (lub przynajmniej Android Studio) posiada także ostrzeżenie dla tej sytuacji:

zmienna użyta tylko w następujących zwrotu i może być inlined

Ten Sprawozdania z inspekcji zmiennych lokalnych albo używany tylko w bardzo następny zwrot lub dokładne kopie innych zmiennych. W obu przypadkach lepiej wstawić taką zmienną.


Nie sądzę wydajność jest coś się martwić w ogóle w tej sytuacji. Biorąc to pod uwagę, jak wspomniał @Clashsoft w swoim komentarzu, JIT najprawdopodobniej zainicjuje zmienną, a otrzymasz taki sam wynik w obu kierunkach.

+0

Dziękuję za to, ma to sens i myślę, że postąpię zgodnie z tą zasadą. – SJ19

+12

Nie zgadzam się z regułą SSLR. Często powoduje to, że metody są łatwiejsze do debugowania, ponieważ przed powrotem z metody istnieje etap w debugerze, w którym można zaobserwować wynik. Kosztem tego jest zmienna lokalna na stosie .... w zasadzie za darmo do czyszczenia. – Jared

+3

Niekoniecznie potrzebujesz kroku przed instrukcją 'return', aby sprawdzić wartość, zanim zostanie zwrócona podczas debugowania. Uważam, że ważniejsze jest mieć czystszy kod niż nieco łatwiejszy do debugowania. W przeciwnym razie zawsze mielibyśmy wiele zmiennych lokalnych, aby pomóc nam w debugowaniu każdego wyniku operacji. –

4

Kompilatory są zwykle na tyle inteligentne, aby optymalnie optymalizować tego typu rzeczy. Zobacz data-flow optimizations na Wikipedii.

W tym przypadku prawdopodobnie trzeba będzie przydzielić tymczasową zmienną, aby zapisać wynik, nawet jeśli nie określisz go samodzielnie.

Edit: Clashsoft ma rację kompilator kodu bajtowego:

$ cat a.java 
class a { 
    public static int a(int x, int y) { 
    return x/y; 
    } 

    public static int b(int x, int y) { 
    int r = x/y; 
    return r; 
    } 

    public static int c(int x, int y) { 
    final int r = x/y; 
    return r; 
    } 
} 
$ javap -c a 
Compiled from "a.java" 
class a { 
    a(); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 

    public static int a(int, int); 
    Code: 
     0: iload_0 
     1: iload_1 
     2: idiv 
     3: ireturn 

    public static int b(int, int); 
    Code: 
     0: iload_0 
     1: iload_1 
     2: idiv 
     3: istore_2 
     4: iload_2 
     5: ireturn 

    public static int c(int, int); 
    Code: 
     0: iload_0 
     1: iload_1 
     2: idiv 
     3: istore_2 
     4: iload_2 
     5: ireturn 
} 
+1

Sam kompilator kodu bajtowego Java prawdopodobnie tego nie wykryje, chyba że zadeklarujesz zmienną jako 'final', ale wątpię, aby kompilator JIT nie wstawił zmiennej. – Clashsoft

+1

Może powinieneś dodać trzeci przypadek, w którym zmienna jest zadeklarowana jako "ostateczna". –

+0

Dzięki. 'final' lub nie, nadal robi różnicę. Teraz zastanawiam się, czy JIT naprawdę się tym zajmuje. –

4

Wybierz wersję myślisz jest bardziej czytelny.

Istnieją uzasadnione przypadki, w których wymieniona zmienna poprawia czytelność. Na przykład:

public String encrypt(String plainString) 
{ 
    byte[] plainBytes  = plainString.getBytes(StandardCharsets.UTF_8); 
    byte[] hashPlainBytes = enhash(plainBytes, 4); 
    byte[] encryptedBytes = doAes128(Cipher.ENCRYPT_MODE , hashPlainBytes); 
    String encryptedBase64 = Base64.getEncoder().withoutPadding().encodeToString(encryptedBytes); 
    return encryptedBase64; 
} 

public String decrypt(String encryptedBase64) 
{ 
    byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64); 
    byte[] hashPlainBytes = doAes128(Cipher.DECRYPT_MODE , encryptedBytes); 
    byte[] plainBytes  = dehash(hashPlainBytes, 4); 
    String plainString = new String(plainBytes, StandardCharsets.UTF_8); 
    return plainString; 
} 

Istnieją również przypadki, w których potrzebujemy zmiennej w innym typie niż typ zwrotu. Wpływa to na konwersję i wnioskowanie typu, co powoduje znaczącą różnicę semantyczną.

Foo foo()   vs.  Foo foo() 
{        { 
            Bar bar = expr; 
    return expr;     return bar; 
}        }