4

Давайте представим, что есть некий метод, который принимает скажем 3 параметра.

public void something(final String id, final String name, final Entity entity){
    //...
}

В данном случае все параметры помечены как final. Т.е. я точно знаю, что не буду их изменять, а только буду пользоваться их значениями. Какие + и - описанного выше. Стоит ли писать слово 'final' в параметрах метода? Имеет ли это какой-либо смысл в плане оптимизации?

  • 1
    да, стоит и да, имеет смысл в любом случае. Минусов нет, кроме очевидного - невозможность изменить значение. Кроме того, в анонимный класс можно передать только final-значения, если вдруг такое потребуется. – pavlofff Dec 17 '17 at 09:51
  • @pavlofff , пардон, какая оптимизация здесь есть ? – aleshka-batman Dec 17 '17 at 09:58
  • @aleshka-batman Компилятор, зная, что имеет дело с константами, может оптимизировать свою работу например, но в основном это относится к безопасности кода – pavlofff Dec 17 '17 at 10:00
  • @pavlofff а можно поподробнее? Желательно со ссылками на соответствующие разделы документации и/или кода HotSpot. А то я пару лет назад пытался выяснить по поводу оптимизаций для финальных аргументов и ничего найти не смог. – Sergey Gornostaev Dec 17 '17 at 10:11
  • @SergeyGornostaev Это предположение, а не утверждение. Я не настолько силен в Java или имею настолько много свободного времени, чтобы погружаться в такие тонкости :) – pavlofff Dec 17 '17 at 13:12
  • по-моему, это какие-то пережитки прошлого или атавизмы более ранних языков. Я никогда не юзаю final параметры, т.к. не имею практики пользоваться стековыми переменными. Если нужно, заведу новую. Мы же не в 90-х живем, сейчас памяти побольше чтоб так экономить. –  Dec 17 '17 at 14:16
  • @pavlofff - в анонимный класс можно передавать любые переменные, но если вы их там собираетесь изменять, то они должны быть EFFECTIVELY final, а не просто final. –  Dec 17 '17 at 14:17
  • @ОлексійМоренець Нет, вы не можете передать в анонимный класс не final-значения основного класса, тем более вы не можете изменять final-значения. effectively final ввели только в Java 8, на android он ,например, не доступен (в Java EE тоже в основном Java 6 еще) – pavlofff Dec 17 '17 at 14:58
  • public static void doSmth(int i) { ActionListener listener = event -> { System.out.println(i); // так нельзя??? }; } –  Dec 17 '17 at 19:27
  • @ОлексійМоренець Вот о чем речь и да, до Java 8 код из вопроса в ссылке не скомпилируется без объявления final (в Java 8 вы тоже можете использовать свойство effectively final в анонимном классе или лямбде с тем ограничением, что объявленая переменная нигде не изменяется, то есть фактически константа, хотя и явно не указана таковой, так что что вы там изменять планируете в отношении final-значений, я так и не понял). – pavlofff Dec 18 '17 at 10:27
  • @pavloff . наверное, мы говорим о разных вещах. Для меня final переменная это та, которая объявлена с кл словом final. А вы наверное рассматриваете возможность изменения значения переменной, переданной как параметр. –  Dec 18 '17 at 11:32
  • @ОлексійМоренець Для меня тоже. Вы написали: "если вы их там собираетесь изменять, то они должны быть EFFECTIVELY final, а не просто final" - понять смысл этой фразы мне не удалось. Изменять значение переменной основного класса в анонимном классе нельзя в любом случае, а отмеченных final и в основном тоже. Так же при изменении значения переменной она уже не сможет быть effectively final – pavlofff Dec 18 '17 at 11:35
  • Я об этом и написал ))) –  Dec 18 '17 at 11:54

1 Answers1

7

Вот такой класс

public class Final {
    private final String second;
    private String first;

    public Final(String first, final String second) {
        this.first = first;
        this.second = second;
    }
}

Компилируется в вот такой байткод

// class version 52.0 (52)
// access flags 0x21
public class Final {

  // compiled from: Final.java

  // access flags 0x12
  private final Ljava/lang/String; second

  // access flags 0x2
  private Ljava/lang/String; first

  // access flags 0x1
  public <init>(Ljava/lang/String;Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 6 L1
    ALOAD 0
    ALOAD 1
    PUTFIELD Final.first : Ljava/lang/String;
   L2
    LINENUMBER 7 L2
    ALOAD 0
    ALOAD 2
    PUTFIELD Final.second : Ljava/lang/String;
   L3
    LINENUMBER 8 L3
    RETURN
   L4
    LOCALVARIABLE this LFinal; L0 L4 0
    LOCALVARIABLE first Ljava/lang/String; L0 L4 1
    LOCALVARIABLE second Ljava/lang/String; L0 L4 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Как видно, в сгенерированном байткоде, у final String second исчез модификатор final - Ljava/lang/String;

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

rjhdby
  • 13,850
  • а с другими типами что, так же? стринг по умолчанию - final, даже если явно не указывать – pavlofff Dec 17 '17 at 11:02
  • @pavlofff в принципе можете и сами проверить, но да - так же. – rjhdby Dec 17 '17 at 11:14
  • 3
    @pavlofff String не final, он immutable, это разные вещи. –  Dec 17 '17 at 14:14