【问题标题】:Anonymous vs named inner classes? - best practices?匿名与命名内部类? - 最佳实践?
【发布时间】:2009-04-03 16:06:12
【问题描述】:

我有一个类,我们称之为 LineGraph,它呈现一个折线图。我需要对它进行子类化,但派生类只在一个地方使用,并且与使用它的类耦合。所以我使用的是内部类。

我看到了两种方法:

匿名内部类

public class Gui {
    LineGraph graph = new LineGraph() {
        // extra functionality here.
    };
}

命名内部类

public class Gui {
    MyLineGraph graph = new MyLineGraph();

    private class MyLineGraph extends LineGraph {
        // extra functionality here.
    }
}

我不是匿名内部类的粉丝,因为坦率地说我只是觉得它看起来很丑。但是对于只在一个地方使用的子类,命名内部类是否过大?公认的做法是什么?

【问题讨论】:

    标签: java oop class inheritance anonymous-class


    【解决方案1】:

    匿名内部类的一个优点是没有人可以在其他任何地方使用它,而命名的内部类可以使用(如果只有创建它的类如果设为私有)。这是一个很小的区别,但它确实意味着您可以保护内部类不被其他地方意外使用。

    此外,使用匿名内部类可以让任何阅读您的代码的人都醒悟——“这个类只在这里使用,其他任何地方都没有。”如果你看到一个命名的内部类,有人可能会认为它会在类中的多个地方使用。

    它们非常相似,所以这两个点都不会改变游戏规则。我只是认为,如果您一次性使用匿名内部类,并在类中多次使用命名内部类时,这有助于清晰。

    【讨论】:

    • +1 好点,但请记住,类也可以在函数内部定义。这限制了它们的范围,并且可以很好地暗示它们具有非常本地化的用途。
    【解决方案2】:

    (反驳丹尼尔·卢)

    匿名内部类的一个缺点是没有人可以在其他任何地方使用它,而命名的内部类可以使用(如果只有创建它的类如果设为私有)。这是一个很小的区别,但它确实意味着您可以帮助确保内部类不会在其他地方意外重新创建。

    此外,使用匿名内部类会使任何人阅读您的代码更加困难,因为他们必须解析这个不知从何而来的类。使用命名的内部类,您可以更多地组织源代码。

    我见过有两个(或更多)匿名内部类具有完全相同代码的情况。尤其是在 GUI 中(您可能有多个控件执行相同的操作),这可能会突然出现(我说的是生产代码,而不是我的学生编写的代码)。

    可读性问题是双向的,有些人发现匿名内部类更好,因为它可以让你在一个地方看到正在发生的事情,而另一些人则认为它会分散注意力。这部分取决于个人喜好。

    另外,使类静态更有效,如果您在实例中声明一个匿名内部类,那么将会有更多开销,如果您不需要访问实例变量,这是浪费的(但可能不是在它出现问题之前值得担心)。

    我个人的偏好是使用非匿名类,因为它们可以在以后修改代码时提供更大的灵活性。

    【讨论】:

    • 这就是我觉得匿名内部类的丑陋之处。您正在阅读代码,突然在一堆字段中间有这个类定义。
    • 我在匿名类中对齐代码时总是遇到问题。不管我怎么努力,代码仍然看起来很难看:D
    • 我同意 - 如果有很多匿名内部类,我会发现代码难以阅读/调试。而不是在多个函数中声明匿名内部类(YUCK!),我发现将文件的部分专用于命名的内部类(即文件的底部)是更好的做法。
    • 如果我做 public MyInterface myInterfaceVariable = new MyInterface(){ public void myMethod(){ print("HI") } };现在我可以在该类的任何地方使用 myInterfaceVariable 对象
    【解决方案3】:

    为什么需要对它进行子类化?如果只是覆盖现有的虚方法,我觉得匿名内部类是可以的。如果您要添加额外的功能,我会使用命名类。不过,我会将其设为嵌套类(即使用 static 修饰符)-我发现它们更容易推理:)

    【讨论】:

    • 我的例子有些简化。在实际代码中,它覆盖了 5 个(非抽象)方法。
    • 这听起来足以让它成为一个命名类。我喜欢我的匿名课程很小。
    • 同意。如果它们真的很简单,我通常只使用匿名类。不止一个小函数定义,我将把它变成一个普通的内部类(如果可能的话是静态的)。这样更容易阅读。
    • 看起来你不应该使用继承,而是让 LineGraph 成为一个接口并使用不同的实现(可能委托给你现在的基类)。
    【解决方案4】:

    Do the simplest thing that could possibly work:使用匿名内部类。

    如果您以后发现需要更广泛的范围,请重构代码以支持这一点。

    (你可以对变量做同样的事情——把它们放在最具体的范围内。对其他源资产做同样的事情是有意义的。)

    【讨论】:

      【解决方案5】:

      匿名内部类很难在 Eclipse 中调试(这就是我使用的)。您将无法通过右键单击来查看变量值/注入值。

      【讨论】:

        【解决方案6】:

        内部类的一个缺点是它们不能是静态的。这意味着它将持有对包含它们的外部类的引用。

        非静态内部类可能是个问题。例如,我们最近有一个内部类被序列化,但外部类不可序列化。隐藏的引用意味着外部类也将被序列化,这当然失败了,但需要一段时间才能找出原因。

        在我工作的地方,我们的编码最佳实践(如果可能)鼓励​​使用静态内部类,因为它们携带的隐藏包袱更少并且更精简。

        【讨论】:

          【解决方案7】:

          我个人的经验法则:如果匿名内部类要小,请坚持使用匿名类。小被定义为大约 20 - 30 行或更少。 如果它要更长,我认为它开始变得不可读,所以我将它命名为内部类。 我记得曾经看过一个 4000+ 行的匿名内部类。

          【讨论】:

            【解决方案8】:

            匿名内部类通常是要走的路。我发现它们非常具有可读性。但是,如果该类的实例需要序列化(即使只是因为它是其他东西的字段),我强烈建议使用命名的内部类。字节码中匿名内部类的名称很容易更改,这会破坏序列化。

            【讨论】:

              【解决方案9】:

              匿名类不能有构造函数,因为它们没有名字。如果您需要传递其他变量而不是您正在扩展的类的构造函数中的变量,您应该使用(静态)命名的内部类。这有时可以通过在周围的方法/代码中使用 final 变量来克服,但这有点难看恕我直言(并且可能导致 Robin 所说的)。

              【讨论】:

                【解决方案10】:

                我对简单的匿名类没有意见。但如果它由多于几行代码或几个方法组成,内部类就更清晰了。我还认为,在某些情况下,它们永远不应该被使用。比如什么时候必须返回数据。

                我看过代码,其中使用 1 个项目的最终数组将数据从调用传回匿名内部类。在 anon 类的方法中,设置单个元素,然后在方法完成后提取此“结果”。有效但丑陋的代码。

                【讨论】:

                  【解决方案11】:

                  匿名类:

                  • 定义中不能有任何static(静态类、静态字段、静态初始化器等)
                  • 无法在 Eclipse 中检查字段
                  • 不能用作类型(Foo$1 myFoo=new Foo$1(){int x=0;} 不起作用)
                  • 无法使用 Eclipse 中的类名定位(搜索 Foo$1 对我不起作用)
                  • 不能重复使用

                  但是,匿名类可以有一个像 {super.foo(finalVariable+this.bar);} 这样的初始化器

                  命名内部类没有这些限制,但即使一个专门用于长过程的中间,声明也必须向上移动到下一个命名类。

                  如果限制不适用,我个人更喜欢匿名内部类,因为:

                  • 我知道任何其他字段仅在类定义中引用。
                  • Eclipse 可以轻松地将它们转换为嵌套类。
                  • 我不需要复制我想使用的变量。我可以将它们声明为最终并引用它们。这在我有很多参数时特别有用。
                  • 交互的代码紧密相连。

                  【讨论】:

                    【解决方案12】:

                    我认为在这种情况下你所做的一切都非常有意义,无论你怎么看,我认为你真的在这个特殊问题上分裂了头发。它们都非常相似,并且都可以工作。

                    【讨论】:

                    • 很多时候两个选项都可以工作,但一个比另一个更易读和维护。我认为在对最佳方式存有疑问时征求其他人的意见是很有意义的。
                    【解决方案13】:

                    我认为这是一个品味问题。我更喜欢为Functors 使用匿名类。但在你的情况下,我会使用一个内部类,因为我认为你将在其中打包不仅仅是几行代码,也许不仅仅是一个方法。它会强调这样一个事实,即它通过将其放在类中而不是隐藏在方法中的某个位置来向超类添加功能。另外,谁知道呢,也许有一天你可能需要其他地方的子类。当然,这取决于您对软件将如何发展的了解程度。除此之外,只需掷硬币。 :)

                    【讨论】:

                      【解决方案14】:

                      今天刚和我的同事讨论过这个问题,正在四处寻找流行的意见。

                      我同意 TofuBeer。如果您的代码超过 2 行,则它可能不是“一次性的”,并且可能有一天会被重用。如果您使用 MVC 模式创建表单,则页面上可能有 20 个可控元素,而不是用大量匿名类使视图混乱(见鬼,您的视图可能是使用 GUI 构建器创建的,并且代码是自动生成的)编辑源甚至不是一个选项)您可能会将每个元素提供给控制器。您可以在控制器中嵌套内部类,以处理视图所需的每个不同的处理程序/侦听器接口。这很好地组织了代码,特别是如果您有一个约定,即您的处理程序类必须使用 GUI 元素名称命名(如 backButtonHandler 用于 backButtonElement)。这对我们来说非常有效,我们编写了一个自动注册工具(当您在视图上注册控制器时,视图使用元素名称查找内部类并将它们用于每个命名元素的处理程序)。这对于匿名类是不可能的,并且使控制器更易于回收。

                      TLDR:如有疑问,请编写一个命名的内部类。您永远不会知道有一天是否有人想重用您的代码(除非它是 2 行代码,那么您必须想知道“这段代码有味道吗?”)。从长远来看,组织良好的代码具有更高的收益,尤其是在您的项目处于维护状态时。

                      【讨论】:

                        【解决方案15】:

                        对于匿名内部类,我们无法更改包含类成员的状态。它们需要被声明为 final。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2010-10-09
                          • 2011-08-09
                          • 2011-04-21
                          • 2015-05-28
                          • 1970-01-01
                          • 2022-11-10
                          • 1970-01-01
                          • 2011-11-05
                          相关资源
                          最近更新 更多