匿名内部类 vs 命名内部类?最佳实践?

49

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

我不喜欢匿名内部类,因为我觉得看起来非常丑陋。但在仅在一个地方使用的子类情况下,是否使用命名内部类有些过度?那么,接受的惯例是什么?

16个回答

55

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

此外,使用匿名内部类可以让任何阅读您代码的人提前了解 - "这个类仅在这里使用,没有在其他地方使用。" 如果您看到一个命名的内部类,有人可能会认为它会在类中的多个位置使用。

它们非常相似,因此这两个观点都不是决定性的。我只是认为,如果您要进行一次性操作,请使用匿名内部类以提高清晰度;如果在类中使用多次,请使用命名内部类。


6
好的观点,但要记住类也可以在函数内定义。这限制了它们的作用域,可能是它们具有非常局限性用途的一个很好的提示。 - Tim Frey

39

(反驳丹尼尔·刘Daniel Lew的观点)

匿名内部类的一个缺点是它不能在其他地方使用,而命名内部类可以使用(即使只有创建它的类可以使用,如果将其私有化)。这是一个小区别,但它确实意味着您可以确保内部类不会在其他地方意外重复创建。

此外,使用匿名内部类会让阅读代码的任何人更加困难,因为他们需要解析突然出现的这个类。使用命名内部类,您可以更好地组织源代码。

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

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

此外,如果您在实例中声明匿名内部类,则将该类静态化更有效率,因为这样会增加开销,如果您不需要访问实例变量,则会浪费资源(但直到出现问题才值得担心)。

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


4
这就是我认为匿名内部类丑陋的原因。当你阅读代码时,突然在一堆字段中间出现了这个类定义。 - Joe Attardi
我总是有问题将匿名类中的代码对齐。无论我多么努力,代码看起来还是很丑:D - janetsmith
我同意 - 如果有很多匿名内部类,我发现代码很难阅读/调试。与在多个函数中声明匿名内部类(呕吐!)不同,我发现将文件的某些部分专门用于命名内部类,即文件底部,是更好的实践。 - Someone Somewhere
如果我这样做:public MyInterface myInterfaceVariable = new MyInterface(){ public void myMethod(){ print("HI") } }; 那么我可以在该类的任何地方使用myInterfaceVariable对象。 - QAMAR

10
为什么需要子类化?如果只是为了重写现有的虚拟方法,使用匿名内部类也可以。如果要添加额外的功能,我会使用命名类。但我会将其作为嵌套类(即使用 static 修饰符)- 我发现它们更容易理解 :)

我的例子有点简化了。在真实的代码中,它覆盖了5个(非抽象)方法。 - Joe Attardi
这听起来足够值得将其制作成一个命名类。我喜欢我的匿名类很小。 - Jon Skeet
1
同意。如果匿名类非常简单,我通常只使用它们。如果有一个以上的小函数定义,我会将其变成普通的内部类(如果可能,则为静态)。这样更容易阅读。 - Herms
看起来你不应该使用继承,而是将LineGraph作为一个接口,并使用不同的实现(可能委托给你现在的基类)。 - Jacek Szymański

9

做最简单的事情,可能会起作用:使用匿名内部类。

如果您后来发现需要更广泛的范围,则重构代码以支持此操作。

(您可以将变量放在最特定的范围内。将其他源资产放在同一范围内是有意义的。)


5

我的个人经验法则是:如果匿名内部类很小,就使用匿名类。所谓小指的是大约20-30行或更少。 如果它将会变得更长,依我看来这将会变得难以阅读,因此我会将其转换为命名内部类。 我曾经见过一个4000多行的匿名内部类。


4

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


3
内部类的一个缺点是它们不能是静态的。这意味着内部类将保留对包含它们的外部类的引用。
非静态内部类可能会出现问题。例如,我们最近遇到一个内部类被序列化,但外部类未被序列化的情况。隐藏的引用意味着外部类也会被序列化,这当然会失败,但花费了一些时间才找出原因。
在我的工作中,我们的编码最佳实践鼓励使用静态内部类(如果可能),因为它们携带的隐藏负担更少,更加简洁。

2

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


2

匿名内部类通常是首选的方式。我发现它们非常易读。但是,如果该类的实例需要被序列化(即使只是因为它是其他某个东西的字段),我强烈建议使用命名内部类。匿名内部类在字节码中的名称很容易更改,这可能会导致序列化失败。


1

我对简单的匿名类没有问题。但如果它包含超过几行代码或几个方法,内部类更清晰。我还认为在某些情况下,它们不应该被使用。例如当它们必须返回数据时。

我曾经看到过一些代码,其中使用一个最终数组来从调用匿名内部类的方法中传递数据。在匿名类的方法内部,设置单个元素,然后在方法完成后提取此“结果”。有效,但丑陋的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接