Как & ldquo; final & rdquo; используется в этом случае? (java, android) [дубликат]

Предполагая, что вы хотите сортировать по значению d['key']['subkey'] для каждого словаря d в списке, используйте это:

sorted(yourdata, key=lambda d: d.get('key', {}).get('subkey'), reverse=True)
302
задан Andrii Abramov 14 July 2017 в 09:29
поделиться

11 ответов

Как отмечено в комментариях, некоторые из них становятся неактуальными в Java 8, где final может быть неявным. Только конечная переменная эффективно может использоваться в анонимном внутреннем классе или лямбда-выражении.


Это в основном из-за того, что Java управляет закрытием .

Когда вы создаете экземпляр анонимного внутреннего класса, любые переменные, которые используются в этом классе, имеют свои значения , скопированные через автогенерированный конструктор. Это позволяет компилятору автоматически генерировать различные дополнительные типы для хранения логического состояния «локальных переменных», как, например, компилятор C # ... (Когда C # захватывает переменную в анонимной функции, она действительно захватывает переменную - замыкание может обновить переменную таким образом, что это видно основной части метода, и наоборот.)

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

Создание окончательной переменной устраняет все эти возможности - поскольку значение не может вообще не измениться, вам не нужно беспокоиться о том, будут ли такие изменения видны. Единственные способы, позволяющие методу и анонимному внутреннему классу видеть изменения друг друга, - использовать изменяемый тип некоторого описания. Это может быть сам охватывающий класс, массив, изменяемый тип оболочки ... что-то в этом роде. В принципе, это немного похоже на связь между одним и тем же способом: изменения, внесенные в параметры одного метода, не видны его вызывающей стороне, но изменения, внесенные в объекты , относящиеся к по параметрам.

Если вам интересно более подробное сравнение между закрытием Java и C #, у меня есть статья , которая идет дальше. Я хотел сосредоточиться на стороне Java в этом ответе:)

428
ответ дан Jon Skeet 16 August 2018 в 05:46
поделиться
  • 1
    Да. В принципе, поддержка полного закрытия может быть реализована путем перемещения всех переменных, на которые ссылаются в специальном автогенерированном классе. – Ivan Dubrov 19 January 2011 в 08:14
  • 2
    @Ivan: Как и C #, в основном. Он обладает довольно высокой степенью сложности, хотя, если вам нужна такая же функциональность, как и C #, где переменные из разных областей могут быть «созданы» разное количество раз. – Jon Skeet 19 January 2011 в 08:15
  • 3
    @Ustaman: Я подозреваю, что вы могли - но это слишком много, чтобы входить в комментарии. Я предлагаю вам задать новый вопрос с некоторым примером кода. – Jon Skeet 16 September 2011 в 22:07
  • 4
    Все это было справедливо для Java 7, имейте в виду, что с Java 8 были введены блокировки, и теперь действительно возможно получить доступ к нечетному полю класса из его внутреннего класса. – Mathias Bader 24 October 2014 в 15:41
  • 5
    @MathiasBader: Действительно? Я думал, что это по сути тот же механизм, теперь компилятор достаточно умен, чтобы сделать вывод final (но он все равно должен быть окончательным). – Thilo 4 March 2016 в 08:56

Вы можете создать переменную уровня класса для получения возвращаемого значения.

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

теперь вы можете получить значение K и использовать его там, где хотите. экземпляр привязан к основному классу и может получить доступ к конечным локальным переменным его содержащего метода. Когда экземпляр использует конечный локальный из его содержащего метода, переменная сохраняет значение, которое оно удерживало во время создания экземпляра, даже если переменная вышла за пределы области (это, по сути, грубая ограниченная версия закрытия Java).

Поскольку локальный внутренний класс не является членом класса или пакета, он не объявляется с уровнем доступа. (Однако ясно, что его собственные члены имеют уровни доступа, как в обычном классе.)

40
ответ дан Andrii Abramov 16 August 2018 в 05:46
поделиться
  • 1
    спасибо за Ваш ответ. Я знаю все это, и мое решение лучше, чем это. мой вопрос «почему только окончательный» ? – user 19 January 2011 в 08:10
  • 2
    Я упомянул, что «без сохранения его как частного участника» – user 19 January 2011 в 08:11
  • 3
    Тогда ответ заключается в том, как они реализованы :) – Ivan Dubrov 19 January 2011 в 08:12
  • 4
    Благодарю. Я использовал трюк выше самостоятельно. Я не был уверен, что это хорошая идея. Если Java не позволяет это, это может быть веской причиной. В вашем ответе уточняется, что мой код List.forEach безопасен. – RuntimeException 30 October 2015 в 12:54
  • 5
    Прочитайте stackoverflow.com/q/12830611/2073130 для хорошего обсуждения обоснования «почему только окончательный». – lcn 9 December 2015 в 00:24
  • 6
    Вы также можете использовать, например, AtomicReference или AtomicInt для этой же цели ... – csharpfolk 6 August 2017 в 12:35
private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}
1
ответ дан Bo Persson 16 August 2018 в 05:46
поделиться

Анонимный класс является внутренним классом , и строгое правило применяется к внутренним классам (JLS 8.1.3) :

Любая локальная переменная, параметр формального метода или параметр обработчика исключений, используемые, но не объявленные во внутреннем классе, должны быть объявлены окончательными. Любая локальная переменная, используемая, но не объявленная во внутреннем классе, должна быть явно назначена перед телом внутреннего класса.

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

( У Джона есть полный ответ - я сохраняю его одним, потому что его может заинтересовать правило JLS)

16
ответ дан Community 16 August 2018 в 05:46
поделиться

Попробуйте этот код,

Создайте список массивов и поместите значение внутри него и верните его:

private ArrayList f(Button b, final int a)
{
    final ArrayList al = new ArrayList();
    b.addClickHandler(new ClickHandler() {

         @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             al.add(b);
        }
    });
    return al;
}
0
ответ дан Jouby 16 August 2018 в 05:46
поделиться
  • 1
    ОП задает причины, почему требуется что-то. Следовательно, вы должны указать, как ваш код обращается к нему – NitinSingh 18 July 2018 в 10:21

Поскольку Jon имеет детали реализации, ответ на другой возможный ответ будет состоять в том, что JVM не хочет обрабатывать запись в записи, которая завершила его активацию.

Рассмотрим Если ваш lambdas вместо того, чтобы быть примененным, хранится в некотором месте и запускается позже.

Я помню, что в Smalltalk вы получите незаконный магазин, созданный, когда вы сделаете такую ​​модификацию.

0
ответ дан mathk 16 August 2018 в 05:46
поделиться

Когда внутри тела метода определен анонимный внутренний класс, все переменные, объявленные окончательными в объеме этого метода, доступны из внутреннего класса. Для скалярных значений после его назначения значение конечной переменной не может измениться. Для значений объекта ссылка не может быть изменена. Это позволяет компилятору Java «захватить» значение переменной во время выполнения и хранить копию как поле во внутреннем классе. Когда внешний метод завершен и его стек стека удален, исходная переменная исчезла, но частная копия внутреннего класса сохраняется в собственной памяти класса.

( http: //en.wikipedia .org / вики / Final_% 28Java% 29 )

2
ответ дан ola_star 16 August 2018 в 05:46
поделиться

Методы внутри монономического внутреннего класса могут быть вызваны после того, как поток, который породил его, завершился. В вашем примере внутренний класс будет вызываться в потоке отправки событий, а не в том же потоке, что и его созданный. Следовательно, объем переменных будет различным. Поэтому, чтобы защитить такие проблемы с областью выделения переменных, вы должны объявить их окончательными.

2
ответ дан S73417H 16 August 2018 в 05:46
поделиться

Причина, по которой доступ ограничивается только локальными конечными переменными, заключается в том, что если все локальные переменные станут доступными, они сначала должны быть скопированы в отдельный раздел, где внутренние классы могут иметь к ним доступ и поддерживать несколько копий изменяемых локальных переменных могут приводить к несогласованным данным. В то время как конечные переменные неизменяемы и, следовательно, любое количество копий для них не окажет никакого влияния на согласованность данных.

5
ответ дан Swapnil Gangrade 16 August 2018 в 05:46
поделиться
  • 1
    Это не так, как это реализовано на таких языках, как C #, которые поддерживают эту функцию. Фактически, компилятор изменяет переменную из локальной переменной в переменную экземпляра или создает дополнительную структуру данных для этих переменных, которая может нарушить объем внешнего класса. Однако нет «множественных копий локальных переменных», – Mike76 27 August 2016 в 13:13
  • 2
    Mike76 Я не смотрел реализацию C #, но Scala делает вторую вещь, о которой вы говорили, я думаю: если Int переназначается внутри замыкания, измените эту переменную на экземпляр IntRef (по существу, изменяемый Integer обертка). Каждый переменный доступ затем переписывается соответствующим образом. – Adowrath 15 September 2017 в 07:30

Возможно, этот трюк дает идею

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);
-2
ответ дан Whimusical 16 August 2018 в 05:46
поделиться

Ну, в Java переменная может быть окончательной не только как параметр, но как поле уровня класса, например

public class Test
{
 public final int a = 3;

или как локальная переменная, например

public static void main(String[] args)
{
 final int a = 3;

Если вы хотите получить доступ и изменить переменную из анонимного класса, вам может потребоваться сделать переменную переменной уровня класса в классе-оболочке.

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

Вы не можете имеют переменную как окончательную и дают ей новое значение. final означает именно это: значение неизменное и окончательное.

И поскольку он окончательный, Java может безопасно скопировать его в локальные анонимные классы. Вы не получаете ссылку в int (тем более, что вы не можете иметь ссылки на примитивы, такие как int в Java, просто ссылки на объекты).

Он просто копирует значение a в неявное int, называемое a в вашем анонимном классе.

7
ответ дан Zach L 16 August 2018 в 05:46
поделиться
  • 1
    Я связываю «переменную уровня класса». с static. Может быть, более понятно, если вы используете «переменную экземпляра». вместо. – eljenso 19 January 2011 в 13:23
  • 2
    ну, я использовал класс, потому что этот метод будет работать как с экземплярами, так и со статическими переменными. – Zach L 19 January 2011 в 18:01
Другие вопросы по тегам:

Похожие вопросы: