Define final variable in java

Вот так final…

Java-университет

Вот так final… - 1

В java есть ключевое слово – final . Оно может применяться к классам, методам, переменным (в том числе аргументам методов). Для класса это означает, что класс не сможет иметь подклассов, т.е. запрещено наследование. Это полезно при создании immutable (неизменяемых) объектов, например, класс String объявлен, как final .

 public final class String < >class SubString extends String < //Ошибка компиляции >

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

 public class SuperClass < public final void printReport()< System.out.println("Report"); >> class SubClass extends SuperClass < public void printReport()< //Ошибка компиляции System.out.println("MyReport"); >> 

Для переменных примитивного типа это означает, что однажды присвоенное значение не может быть изменено. Для ссылочных переменных это означает, что после присвоения объекта, нельзя изменить ссылку на данный объект. Это важно! Ссылку изменить нельзя, но состояние объекта изменять можно. С java 8 появилось понятие — effectively final . Применяется оно только к переменным (в том числе аргументам методов). Суть в том, что не смотря на явное отсутствие ключевого слова final , значение переменной не изменяется после инициализации. Другими словами, к такой переменной можно подставить слово final без ошибки компиляции. effectively final переменные могут быть использованы внутри локальных классов ( Local Inner Classes ), анонимных классов ( Anonymous Inner Classes ), стримах (Stream API).

 public void someMethod() < // В примере ниже и a и b - effectively final, тк значения устанавливаютcя однажды: int a = 1; int b; if (a == 2) b = 3; else b = 4; // с НЕ является effectively final, т.к. значение изменяется int c = 10; c++; Stream.of(1, 2).forEach(s->System.out.println(s + a)); //Ок Stream.of(1, 2).forEach(s-> System.out.println(s + c)); //Ошибка компиляции > 
  1. Что можно сказать про массив, когда он объявлен final ?
  2. Известно, что класс String — immutable , класс объявлен final , значение строки хранится в массиве char , который отмечен ключевым словом final .
 public final class String implements java.io.Serializable, Comparable, CharSequence < /** The value is used for character storage. */ private final char value[]; 
  1. Т.к. массив – это объект, то final означает, что после присвоения ссылки на объект, уже нельзя ее изменить, но можно изменять состояние объекта.
 final int[] array = ; array[0] = 9; //ок, т.к. изменяем содержимое массива – array = new int[5]; //ошибка компиляции 
 import java.lang.reflect.Field; class B < public static void main(String[] args) throws Exception < String value = "Old value"; System.out.println(value); //Получаем поле value в классе String Field field = value.getClass().getDeclaredField("value"); //Разрешаем изменять его field.setAccessible(true); //Устанавливаем новое значение field.set(value, "JavaRush".toCharArray()); System.out.println(value); /* Вывод: * Old value * JavaRush */ >> 

Обратите внимание, что если бы мы попытались изменить подобным образом финальную переменную примитивного типа, то ничего бы не вышло. Предлагаю вам самостоятельно в этом убедить: создать Java класс, например, с final int полем и попробовать изменить его значение через Reflection API. Всем удачи!

Читайте также:  Функция пересечения множеств питон

Источник

Declare final variable, but set later

I know this is fairly simple topic, but I really want to wrap my head around it. This is what I'm trying to do, but it doesn't like the final modifier. Is there another way to achieve the effect I'm looking for? Which is basically that I want to make sure the id can not change durning the Activities entire life.

private final long mId; @Override public void onCreate(Bundle savedInstanceState)

I should point out that this is Android code. Thanks for all the help. I'm not worried about getters or setters or anyone changing my code. The reason I asked was to future proof my code for the next developer to take over. I found this post that also helps shed some light. Android - Activity Constructor vs onCreate

Here's an example of something analagous (but local variable, not a class field) just I stumbled across - it's in the JDK - some code in java.util.PriorityQueue that seems to be initialising a final variable in two steps. In method "public E poll()", a local final variable is declared in line 593 (but not initialised), then initialised in the next line. final int n; final E x = (E) es[(n = --size)];

7 Answers 7

You can set a final variable only in a constructor or in an initializer. Regular methods cannot change the value of variables declared final .

Do you know what is the logic behind this? It would make perfect sense IMO to allow it to be set at exactly one place in the class. . But as I think more about it, if you don't set the value right away, the compiler cannot guarantee that it's been set.

@GregT That's right, if the variable is not set by the time you are done with the constructor, it wouldn't be set at all, even though there may be only one place in the code that sets it. The only way to guarantee that the variable is set only once by the time the constructor is done is to allow it to be set only in the constructor or an initializer (which is merged with all constructors anyway, so it's as good as being a constructor).

You can't. But you can guarantee no external object changes it if it's private and you don't have a setter for it.

Alternatively, you can wrap the long value in another class - LazyImmutableLong . But this is a more verbose approach, and you probably don't need it (note: the class below is not thread-safe)

class LazyImmutableLong < private Long value; public void setValue(long value) < if (this.value != null) < return; // the value has already been set >this.value = value; > public long getValue() > 
private LazyImmutableLong LazyImmutableLong(); public void onCreate(..)

Make it a private instance variable. And just as Bozho said, don't expose a setter method for other classes to call.

And by overkill I mean that I would rather not waste resources allocating, garbage, and function calling

In my code I want to avoid not just external object changes, but also that the value isn't changed inside by accident. That's why I want it to be final in addition to private .

The following Worm (Write-Once-Read-Many) class could help in this kind of scenario.

We could create a nested Wrapper class, that stores the final variable you need. To initialize this variable, you just should call a constructor of the wrapper object. When you call the method getData() , you will get a reference of the final variable in case it is initialized, otherwise, you will get null .

The methods getData() and setData(T data) are required to be thread-safe. To provide it, we use a volatile modifier for the wrapper object. Reading a volatile variable is synchronized and writing to a volatile variable is synchronized, too. Even though some efforts were made to make this code thread-safe I didn't test it in this respect. Depending on the level of thread safety you may consider to make setter and getter synchronized.

public class Worm  < private volatile Wrapperwrapper; public Worm() <> public Worm(T data) throws IllegalAccessError < setData(data); >public T getData() < if (wrapper == null) return null; return wrapper.data; >public void setData(T data) throws IllegalAccessError < if (wrapper != null) throw new IllegalAccessError(); else wrapper = this.new Wrapper<>(data); > @Override public boolean equals(Object obj) < if (this == obj) < return true; >if (obj == null) < return false; >if (getClass() != obj.getClass()) < return false; >final Worm other = (Worm) obj; return Objects.equals(this.getData(), other.getData()); > @Override public int hashCode() < return Objects.hashCode(this.getData()); >final private class Wrapper  < final private T data; Wrapper(T data) < this.data = data; >> > 

Источник

Оцените статью