Java (анонимный или не) внутренние классы: действительно ли хорошо использовать их?

В некоторых моих проектах и в некоторых книгах, как говорили, не использовал внутренний класс (анонимный или нет, статичный или не) - кроме некоторых ограниченных условий, как EventListeners или Runnables - лучшая практика. Они даже были 'forbiden' в моем первом промышленном проекте.

Это - действительно лучшая практика? Почему?

(Я должен сказать, что использую их много...),

- РЕДАКТИРОВАНИЕ---
Я не могу выбрать правильный ответ во всех этих ответах: существует часть справедливости на главным образом всех них: я буду все еще использовать внутренние классы, но я попытаюсь использовать их менее часто!

20
задан 5 revs, 2 users 85% 16 August 2011 в 16:20
поделиться

9 ответов

На мой взгляд, 90% внутренних классов в коде Java являются объектами, которые связаны с одним классом и, таким образом, были «вставлены» в качестве внутренних классов , или анонимные внутренние классы, которые существуют, потому что Java не поддерживает Lambdas.

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

Это оставляет мне необходимые внутренние классы, такие как прослушиватели действий, фальшивое «функциональное» программирование и т. Д. Они часто анонимны, и хотя я не фанат (во многих случаях предпочел бы Lambda), я живу с их, но они не нравятся.

Я не писал ни одного C # годами, но мне интересно, упало ли преобладание внутренних классов или чего-то подобного в C #, когда они представили Lambdas.

27
ответ дан 29 November 2019 в 22:37
поделиться

да, их хорошо использовать, когда вы пытаетесь сохранить целостность класса, и классы никогда не должны создаваться извне их контекста внешнего класса, сделайте конструкторы частными, и у вас действительно хорошая связная инкапсуляция . Любой, кто говорит, что вы должны НИКОГДА их использовать, не знает, о чем они говорят. Для обработчиков событий и других вещей, которые превосходят анонимные внутренние классы, они намного лучше, чем альтернатива загромождению пространства имен вашего пакета множеством обработчиков событий, которые применяются только к определенному классу.

0
ответ дан 29 November 2019 в 22:37
поделиться

Как сказал Мейсон, в синтаксисе Дельфи существует неоднозначность. Где TFoo.Bar - метод, непонятно, что FooValue.Bar означает ссылку на результат вызова TFo.Bar , или указатель метода (или ссылка) TFo.Bar (с подразумеваемым собственным аргументом Foo.

В комментариях к ответу Мейсона Роб Кеннеди, похоже, предлагает компилятору просто разобраться в этом, основываясь на типах всего, о чем идет речь. Это не просто; компилятор уже проделал большую работу, чтобы выяснить, имеется ли в виду ссылка на значение указателя метода или вызов метода. Он фактически анализирует выражения другим способом, когда ожидаемый приемник является типом указателя метода (или ссылки, или указателя функции). Усилия особенно задействованы, когда в картину вносятся перегрузки: компилятор сканирует каждого кандидата перегрузки и проверяет типы указателей метода в каждой позиции параметра, а затем по-разному анализирует аргументы в зависимости от того, содержит ли эта позиция параметра указатель функции в одной из перегрузок. Если перегрузка, требующая указателя функции , не соответствует , компилятор изменяет дерево синтаксического анализа с указателя функции на вызов метода. Механизм перегрузки сам должен определить, какое значение использовать при выполнении сравнения параметров. Это довольно грязно, и было бы здорово, если бы мы не сделали его более беспорядочным.

Оператор префиксного стиля, такой как @ или Addr () , не очень помогает в разрешении этой неоднозначности, не в последнюю очередь потому, что функции могут возвращать указатели функций и так далее; сколько @ нужно запретить неявный (нет необходимости () ) вызов для получения нужного значения? Так что при введении анонимных методов было сделано изменение в синтаксическом анализе выражения: я ввёл возможность использования () для принудительного вызова.

Подробнее об этом можно прочитать здесь:

http://blog.barrkel.com/2008/03/odd-corner-of-delphi-procedural.html

и здесь:

http://blog.barrkel.com/2008/03/procedurally-typed-expressions-redux.html

-121--3853704-

Мне также недавно понадобилось преобразовать XSL в XML-файл. Это F #, который я использовал для этого.

Есть несколько интересных проблем с использованием методов .net.

(* Transforms an XML document given an XSLT. *)

open System.IO
open System.Text
open System.Xml
open System.Xml.Xsl

let path = @"C:\\XSL\\"

let file_xml = path + "test.xml"
let file_xsl = path + "xml-to-xhtml.xsl"

(* Compile XSL file to allow transforms *)
let compile_xsl (xsl_file:string) = new XslCompiledTransform() |> (fun compiled -> compiled.Load(xsl_file); compiled)
let load_xml (xml_file:string) = new XmlDocument() |> (fun doc -> doc.Load(xml_file); doc)

(* Transform an Xml document given an XSL (compiled *)
let transform (xsl_file:string) (xml_file:string) = 
      new MemoryStream()
        |> (fun mem -> (compile_xsl xsl_file).Transform((load_xml xml_file), new XmlTextWriter(mem, Encoding.UTF8)); mem)
        |> (fun mem -> mem.Position <- (int64)0; mem.ToArray())

(* Return an Xml fo document that has been transformed *)
transform file_xsl file_xml
    |> (fun bytes -> File.WriteAllBytes(path + "out.html", bytes))
-121--2814219-

Анонимные внутренние классы имеют преимущества, позволяя видеть поля и переменные вокруг «нового» оператора. Это может сделать для очень чистого дизайна и довольно хороший (но немного многословный) подход к «как мы можем сделать простую версию лямбда-заявлений».

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

3
ответ дан 29 November 2019 в 22:37
поделиться

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

Я бы сказал, что внутренние классы очень полезны.

1
ответ дан 29 November 2019 в 22:37
поделиться

Анонимные внутренние классы часто используются, когда нам нужно реализовать интерфейс с помощью одного метода, например Runnable, ActionListener и некоторых других.

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

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

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

1
ответ дан 29 November 2019 в 22:37
поделиться

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

-121--3302883-

func () и gradc () находятся в правильном виде для функций с неизвестными параметрами. Я уверен, что это был приемлемый синтаксис даже для Unix 6 около 1975 года.

Параметры [] совпадают с параметрами * , как сейчас, так и в прошлом. На самом деле, я помню спор 1980-х годов, который был более уместным:

int main (int argc, char **argv, char **envp)

или

int main (int argc, char *argv[], char *envp[])

Нет другого в действии. Спор более правильный по семантике.

-121--3950488-

Внутренние классы подходят при попытке эмуляции множественного наследования. Это похоже на то, что происходит под капотом с C++: при множественном наследовании в C++ компоновка объекта в памяти фактически является конкатенацией нескольких экземпляров объекта; затем компилятор прорабатывает, как будет корректироваться указатель «this» при вызове метода. В Java отсутствует множественное наследование, но внутренний класс может использоваться для предоставления «представления» данного экземпляра под другим типом.

Большую часть времени можно придерживаться одного наследования, но иногда множественное наследование было бы правильным инструментом, и это время для использования внутреннего класса.

Это означает, что внутренние классы являются каким-то образом более сложными, чем обычные классы, в том же пути, что множественное наследование является более сложным, чем одиночное наследование: многие программисты испытывают некоторые трудности, обворачивая свой ум вокруг этой концепции. Отсюда «лучшая практика»: избегайте внутренних занятий, потому что это смущает ваших коллег. На мой взгляд, это не хороший аргумент, и на моем рабочем месте мы вполне рады использовать внутренние классы, когда считаем это целесообразным.

(Незначительным недостатком внутренних классов является то, что они добавляют один дополнительный уровень отступа в исходном коде. Это немного неудобно иногда, когда хочется сохранить код в пределах 79 столбцов.)

1
ответ дан 29 November 2019 в 22:37
поделиться

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

Как указывает gicappa, анонимные внутренние классы - это наиболее близкие к замыканиям классы Java, и они чрезвычайно подходят для использования в ситуациях, когда передача поведения в метод подходит, если ничего больше.

5
ответ дан 29 November 2019 в 22:37
поделиться

Анонимные классы хорошо использовать при программировании на основе событий, особенно в свинге.

6
ответ дан 29 November 2019 в 22:37
поделиться

Интерполяционная переменная $ cols выглядит так, будто она должна быть числом, скажем 10, поэтому

"%${cols}s"

должна быть интерполирована и эквивалентна

"%10s"

, которая является допустимой строкой формата.

Если бы $ cols был чем-то другим , чем число или допустимая строка формата, вы бы получили предупреждение.

Например, если:

$cols = "w";

, что привело бы к "% ws" в виде строки формата - если вы цитируете ошибку:

Invalid conversion in printf: "%w"

Допустимые сведения о формате можно найти здесь .

-121--3772211-

Я имел дело с Flixel, и я только начал искать с PushButton, который выглядит довольно хорошо, но имеет немного больше сложности, чем мне действительно нужно в данный момент. Но они оба хорошие примеры.

-121--2393400-

Чистота. Легче понять код, если он разбит на логические части, а не на один файл.

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

17
ответ дан 29 November 2019 в 22:37
поделиться
Другие вопросы по тегам:

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