匿名vs命名的内部类? - 最佳做法?

时间:2009-04-03 16:06:12

标签: java oop class inheritance anonymous-class

我有一个类,我们称之为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.
    }
}

我不是匿名内部阶级的粉丝,因为坦率地说,我认为它看起来真的很难看。但是在一个仅在一个地方使用的子类的情况下,一个命名的内部类是否过度杀伤?什么是公认的做法?

15 个答案:

答案 0 :(得分:49)

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

此外,使用匿名内部类可以让任何人阅读你的代码 - “这个类正在这里使用,而不是其他任何地方。”如果你看到一个命名的内部类,有人可能会认为它会在类中的多个地方使用。

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

答案 1 :(得分:36)

(反对Daniel Lew)

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

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

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

可读性问题是双向的,有些人会发现匿名的内部类更好,因为它可以让你看到一次发生的事情,其他人发现它分散了注意力。这部分归结为个人偏好。

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

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

答案 2 :(得分:10)

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

答案 3 :(得分:8)

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

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

(您可以对变量执行相同操作 - 将它们放在最具体的范围内。对其他源资源执行相同操作也是有意义的。)

答案 4 :(得分:4)

匿名内部类很难在Eclipse中调试(这就是我使用的)。只需右键单击即可查看变量值/注入值。

答案 5 :(得分:3)

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

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

在我工作的地方,我们的编码最佳实践(如果可能的话)鼓励静态内部课程,因为它们携带较少的隐藏行李并且更加精简。

答案 6 :(得分:3)

我的个人经验法则:如果匿名内部课程规模较小,请坚持使用匿名课程。小的定义为大约20-30线或更少。 如果它会更长,在我看来它开始变得不可读,所以我把它命名为内部类。 我记得有一次看到4000多行的匿名内部阶级。

答案 7 :(得分:2)

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

答案 8 :(得分:2)

匿名类不能有构造函数,因为它们没有名称。如果您需要传递除正在扩展的类的构造函数中的变量之外的其他变量,则应使用(静态)命名的内部类。这有时可以通过在周围的方法/代码中使用最终变量来克服,但它有点丑陋imho(并且可以导致Robin所说的)。

答案 9 :(得分:1)

我对简单的匿名类没有问题。但如果它由多行代码或几种方法组成,则内部类更清晰。我也认为在某些条件下不应该使用它们。比如当他们必须返回数据时。

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

答案 10 :(得分:1)

匿名课程:

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

但是,匿名类可以使用{super.foo(finalVariable+this.bar);}

之类的初始化程序

命名的内部类没有这些限制,但即使只在长过程中使用了一个,声明也必须移到下一个命名类。

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

  • 我知道任何其他字段仅在类定义中引用。
  • Eclipse可以轻松地将它们转换为嵌套类。
  • 我不需要复制我想要使用的变量。我可以宣布他们是最终的并引用它们。当我有很多参数时,这特别有用。
  • 相互作用的代码非常接近。

答案 11 :(得分:0)

我认为你所做的事情在这种情况下非常有意义,无论你怎么看待它,我认为你真的是在解决这个问题。它们都非常相似,都会起作用。

答案 12 :(得分:0)

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

答案 13 :(得分:0)

今天刚和我的同事讨论这个问题,并且正在寻找流行的观点。

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

TLDR:有疑问的时候写一个命名的内部类。你永远不会知道是否有人想在某一天重新使用你的代码(除非是2行,那你就不得不怀疑'这个代码闻到了吗?')。从长远来看,组织良好的代码具有更高的优势,特别是当您的项目处于维护状态时。

答案 14 :(得分:0)

使用匿名内部类,我们无法更改包含类成员的状态。他们需要被宣布为最终。