Ограничения анонимных классов Java по сравнению с блоками Objective-C

#java #objective-c #closures #block

#java #objective-c #замыкания #блок

Вопрос:

Я только начинаю разбираться в функциях первого порядка и замыканиях после обнаружения блоков в Objective-C. Java — это еще один язык, где я слышал о закрытиях (или их отсутствии) и о том, как анонимные классы в некоторой степени компенсируют это.

Я определенно вижу преимущества замыканий в виде блоков в Objective-C, но каковы ограничения анонимных классов Java? В какой степени они «несколько» компенсируют отсутствие истинных замыканий?

Комментарии:

1. Вы имеете в виду помимо их синтаксической изменчивости?

2. ДА. Эта часть довольно очевидна: P

Ответ №1:

Анонимные классы Java действительно, действительно многословны. Помимо огромного количества шаблонов, которые вам нужны только для их определения, некоторые дизайнерские решения Java означают, что многие общие задачи намного более подробны, чем на других языках. Например, импорт изменяемых повышающих значений в замыкание — это заноза в заднице в Java.

В принципе, Java не поддерживает значения up; вместо этого они моделируются путем передачи их (по значению) в класс через невидимые параметры конструктору класса. Поскольку они передаются по значению, их изменение внутри класса не повлияет на копию в методе, который создал класс, поэтому компилятор заставляет вас объявлять их окончательными, чтобы избежать путаницы. например:

 Runnable function()
{
   final int i = 4;
   return new Runnable()
   {
       public void run()
       {
            System.out.println("i=" i);
            // can't modify i here
       }
   }
}
  

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

 Runnable function()
{
   final int[] i = new int[1];
   i[0] = 4;
   return new Runnable()
   {
       public void run()
       {
            System.out.println("i=" i[0]);
            i[0] = i[0]   1;
       }
   }
}
  

Здесь i сам по себе остается неизменяемым, но поскольку он указывает на изменяемый объект, я могу изменить содержимое объекта. (Естественно, в реальной жизни я бы использовал класс, а не массив, потому что использование массивов действительно уродливо. И это делает его еще более многословным.)

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

Комментарии:

1. Эта функция была удалена из Java 7, которая должна выйти в этом месяце или в следующем, и перенесена на Java 8, которая должна выйти в декабре 2012. Project Lambda

2. или, если вам не нужно разделять состояние между замыканием и областью, в которой оно определено, почему бы просто не присвоить его переменной внутри класса? ` final int i = 4; возвращает new Runnable() { int m = i; public void run() { System.out.println(«m =» m); m ; } } `

3. Это ловкий трюк, о котором я не подумал. Конечно, полезно только в том случае, если вы не хотите экспортировать состояние из внутреннего класса во внешний, но, тем не менее, удобно, и я это запомню. Спасибо!

4. Я хочу сказать, что, поскольку мы сравниваем с блоками Objective-C, многие из упомянутых вами вещей аналогично применимы для блоков Objective-C: блоки по умолчанию захватывают переменные по значению, а захваченные переменные находятся const внутри замыкания; это похоже на то, как анонимные функции Java захватывают по значению и заставляют переменную быть final . Чтобы иметь возможность изменять захваченную переменную в блоках, она должна быть явно объявлена __block в ее исходной области видимости; это похоже на создание массива из 1 элемента в Java для совместного использования состояния, за исключением того, что без явной распаковки.

Ответ №2:

Я действительно не знаю о версии closures для objective-C, но я знаю их из Smalltalk и Lua.

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

Локальные классы Java (частным случаем которых являются анонимные классы) поддерживают ограниченную версию этого: они разрешают доступ к final переменной, то есть переменной, которая не может изменить свое значение. Это реализуется путем копирования значения этой переменной в объект локального класса при его создании. Вы можете эмулировать реальные замыкания, используя изменяемый объект в этой переменной (в простейшем случае одноэлементный массив).

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

Комментарии:

1. Это ключевой момент — хотя внутренние классы Java anon уродливы , они также столь же эффективны, как «красивые замыкания», и сохраняют привязки (конечных переменных, но они могут содержать изменяемые объекты). Scala (с поддержкой функций первого класса) просто скрывает все это утомительное уродство, но работает аналогичным образом под ним.

2. Ах, я хотел упомянуть здесь изменяемые объекты.