2010-02-17 27 views
20

在我的一些项目和某些书籍中,据说不是使用内部类(匿名或不是,静态或不是) - 除了在某些受限制的条件下,如EventListener s或Runnable s - 是最佳做法。他们甚至在我的第一个行业项目中“禁止”。Java(匿名或不内部)内部类:使用它们很好吗?

这真的是最佳做法吗?为什么?

(我必须说,我使用了他们很多...)

- 编辑---
我不能选择一个正确的答案,在所有这些回应:有正当性的一部分主要是所有这些:我仍然会使用内部类,但我会尽量少用它们!

回答

25

在我的观点认为,Java代码中90%的内部类是或者与单个类相关联的实体,因此被“推入”为内部类,或者因Java不支持Lambdas而存在的匿名内部类。

我个人不喜欢看复杂的内部类。它们增加了源文件的复杂性,使它变得更大,它们在调试和分析等方面很难处理。我喜欢将我的项目分成许多包,在这种情况下,我可以使大多数实体成为顶级类只限于包装。这些经常是匿名的,虽然我不是一个粉丝(在很多情况下会更喜欢Lambda),但我活着与他们在一起,但不喜欢他们。

我没有做过任何C#在几年,但我想知道如果内部类或任何时候,他们推出的Lambda表达式C#相当于被丢弃的流行。

+3

为了便于参考,我必须提及** Java 8 **引入了[** Lambda表达式**](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。 – vellotis 2016-01-14 13:03:58

+1

此外,祝你好运单元测试内部类... – twiz 2016-03-29 01:40:19

16

清洁度。如果将代码分解为逻辑代码,并不是所有的代码都会融入相同的文件,那么理解代码会更容易。

这就是说,我不认为明智地使用内部类是不合适的。有时候,这些内部类仅仅是为了一个目的而存在的,所以我不会对它们在使用它们的唯一文件中存在任何问题。但是,这在我的经验中并没有发生。

+2

根据我的经验,只要你做了一个内部类,你就需要它到别的地方:) – Dolph 2010-02-17 21:18:37

+12

如果它被分解成逻辑片断,那么它更容易理解代码,是的 - 但是在*我的书中,这意味着最好定义一些尽可能接近它的地方,而不是将其任意分割成它自己的文件。 ;) – Porculus 2010-02-17 21:41:36

+2

内部类不排除你在他们居住的班级以外使用他们。枚举是一个很好的例子,枚举通常用于单个类,使其成为一个公共静态,然后你有一个上下文的地方,预计将使用枚举。工厂模式是生成实现的内部类的好地方,而不会污染程序包名称空间中的某些事物,这些事物只应在由Factory对象控制的特定情况下实例化。 – 2010-02-17 21:59:39

6

匿名类很适合在基于事件的编程时使用,特别是在swing中。

+1

为什么在基于事件的编程中,而不是其他用法,如用于重新组合众多参数的结构,... 这更多的是教条而不是解释:-1。 – Guillaume 2010-02-22 10:02:24

1

当试图模拟多重继承时,内部类是适当的。它类似于使用C++的情况:在C++中有多重继承时,内存中的对象布局实际上是多个对象实例的串联;然后编译器会调用方法调用时应如何调整“this”指针。在Java中,没有多重继承,但是可以使用内部类来为给定实例提供另一种类型的“视图”。

大多数时候,可以坚持单一继承,但偶尔多重继承是正确的工具,这是使用内部类的时候。

这意味着内部类在某种程度上比普通类更复杂,就像多重继承比单一继承更复杂一样:许多程序员在围绕这个概念时都会遇到一些麻烦。因此,“最佳实践”:避免内部类,因为它会让你的同事感到困惑。在我看来,这不是一个好的论点,在我的工作场所,我们很高兴在我们认为合适的时候使用内部类。

(内部类的小缺点在于,它们加在源代码中缩进的一个额外的水平,这是在时间有点令人厌烦的,当一个希望保持79列中的代码。)

1

当我们需要使用一个方法实现接口时,通常会使用匿名内部类,如Runnable,ActionListener和其他方法。匿名内部类的

一个更伟大的应用是,当你不想做一些类的子类,但你需要重写它的方法一个(或两个)。

当您想要在两个类之间实现紧密的连贯性时,可以使用命名的内部类。它们并不像匿名内部类那样有用,我不能肯定这是一个很好的习惯。

Java也有嵌套(或内部静态)类。当你想提供一些特殊的访问权限时,可以使用它们,标准的公共或默认访问级别是不够的。

0

是的,它是很好的使用它们,当你试图保持一个类的凝聚力,并且类不应该从外部类的外部实例化,使构造函数是私人的,你有非常好的凝聚力封装。任何人都说你应该从不使用它们不知道他们在说什么。对于事件处理程序和匿名内部类擅长的其他事情来说,它们比使用许多仅适用于特定类的事件处理程序混淆包名称空间的替代方法要好得多。

1

内部类通常用于“传递行为”作为方法的参数。具有闭包的其他语言可以很好地支持此功能。 由于语言限制,使用内部类会产生一些不优雅的代码(恕我直言),但它有用并且广泛用于处理事件,并且通常与内部类一起使用。

所以我会说,内部类是非常有用的。

3

匿名内部类能够查看“新”语句周围的字段和变量。这可以设计出一些非常干净的设计,并且对于“我们如何制作一个简单版本的lambda表达式”是一个相当不错的(但有点罗嗦)的方法。

命名内部类具有一个名字,希望告知的权益,其可以以通常的方式进行记录,但其被连接在一起到周围的类。一个非常好的例子是Builder模式,其中内部类负责为初始化过程提供状态,而不是有许多构造函数。这样的构建者不能在类之间重用,所以让构建者与父类紧密联系是非常合理的。

4

是,禁止内部类是一种有益的做法,在找出一个地方禁止他们是警告我关在那里工作,所以保留我的未来理智的好方法。 :)

由于gicappa指出,匿名内部类是最接近的Java必须关闭,且非常适合在情况下使用合格的行为放到一个方法是合适的,如果没有别的。

+1

至少这一个让我微笑,这是一个很好的用法,在公司的编码政策中发现一些愚蠢的规则! – Guillaume 2010-02-22 10:04:23

+0

关于事件处理程序没有具体的内容,嵌套类的主要目的是封装。 – bharatj 2015-09-18 18:14:47

2

某些框架,如Wicket,确实需要匿名内部类。

说永远是愚蠢的。永不说永不!一个好用的例子可能是你的某些遗留代码是由许多类直接在集合字段上操作的人编写的,无论出于何种原因,你都不能更改这些其他类,但需要有条件地将操作镜像到另一个类采集。最简单的做法是通过匿名内部类添加此行为。

bagOfStuff = new HashSet(){ 
    @Override 
    public boolean add(Object o) { 
    boolean returnValue = super.add(o); 
    if(returnValue && o instanceof Job) 
    { 
     Job job = ((Job)o); 
     if(job.fooBar()) 
     otherBagOfStuff.add(job); 
    } 
    return returnValue; 
    } 
} 

也就是说,他们绝对可以像穷人的封闭一样使用。

2

正如一些人所说,很多时候,当你使用一个匿名内部类,它也被用在其他一些地方也...

因此,你可以很容易复制内部类代码很多地方... 当你使用非常简单的内部类来过滤/排序集合,使用谓词,比较器或类似的东西时,这似乎不是问题...

但是你必须知道,当你使用3次匿名内部类完全一样的东西(例如删除一个Collection的“”),你实际上是在java PermGen上创建了3个新类。

因此,如果每个人在任何地方都使用内部类,这可能会导致应用程序拥有更大的permgen。根据应用程序,这可能是一个问题...如果你正在从事的行业,你可能编程嵌入式应用程序具有有限的内存,应该优化...

注意这也是为什么双卷曲括号语法(匿名内部类与非静态初始化块)有时被认为是反模式:

new ArrayList<String>() {{ 
    add("java"); 
    add("jsp"); 
    add("servlets"); 
    }} 

你应该问的人谁禁止你使用它们... 恕我直言,这一切都取决于上下文..

+0

这是反对内心阶层的好处。至少在宣布一些“标准”行为时。 – Guillaume 2011-08-16 16:20:15

+0

这是内心阶层的愚蠢使用。为什么不创建数组列表并添加字符串?显示内部类的一个愚蠢的例子并不能完全利用它们的坏习惯。 – ncmathsadist 2013-12-21 03:16:51

+0

@ncmathsadist我不认为它很好,但有些人发现它更具可读性,并在单元测试中使用它,而permgen并不是什么大问题 – 2013-12-21 11:59:06

3

我建议在使用它时要小心,如果它需要方法参数的话。我刚刚发现与此相关的内存泄漏。它涉及使用GrizzlyContinuation的HttpServlet。
总之这里是bug的代码:

public void doGet(HttpServletRequest request, final HttpServletResponse response){ 
    createSubscription(..., new SubscriptionListener(){ 
    public void subscriptionCreated(final CallController controller) { 
     response.setStatus(200); 
     ... 
     controller.resume(); 
    } 

    public void subscriptionFailed(){ 
     ... 
    } 

    public void subscriptionTimeout(){ 
     ... 
    }}); 
} 

如此以来,听众是通过订阅的HttpServletResponse的也保持的情况下,监听器需要它(不是很明显)保持。然后,只有订阅被删除时,HttpServletResponse实例才会被释放。如果你使用一个内部类来获得它的构造函数中的响应,那么一旦调用恢复释放内存,它就可以被设置为null。

使用它们但要小心!

就是这里所说

马丁

2

一个项目是一个(非静态)内部类进行的引用,它的封闭类。更重要的是,内部类可以访问封闭类的私有成员。它可能会破坏封装。

如果您有选项,请勿使用内部类。

+0

静态内部类?不要如果它是从属于另一个类,使它在同一个文件中的非公共类。没有为监听者使用内部类意味着维护getter的痛苦链,并相互链​​接状态变量,这些变量只是一个丑陋的散列。 – ncmathsadist 2013-12-21 03:18:35