Java中常用内部类吗?它们是否“不好”?

27

Java中内部类常用吗?这与嵌套类是一样的吗?或者在Java中已经被更好的东西所取代了吗?我有一本关于5版本的书,其中有一个使用内部类的例子,但我似乎在某处读到过内部类是“不好”的。

我不知道,希望能听听您的想法。

谢谢。


https://dev59.com/GXVD5IYBdhLWcg3wJIIK - ivan_ivanovich_ivanoff
2
术语:嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。 - Michael Myers
可能重复的问题 https://dev59.com/CnRB5IYBdhLWcg3w3K4J - victor hugo
(a) 是的 (b) 不是 (c) 除非你算上lambda和closure。如果你能引用它们是“不好”的理论来源,请引用,这样我们就可以避免它。 - user207421
8个回答

35

内部类经常被使用,而匿名类 - 类似的东西 - 几乎是必不可少的,因为它们是 Java 最接近闭包的东西。因此,如果您记不住内部类不好的原因,请试着忘记它吧!


2
不要提到双括号初始化器... - skaffman
2
@skaffman - 哎呀!刚刚查了一下,简直不敢相信。你以为你创建了一个List<int>?再猜猜吧,朋友,再猜猜吧! - Daniel Earwicker
@DanielEarwicker 现在Java有匿名类吗? - johnny
@DanielEarwicker 我应该说函数。 - johnny
1
啊,那个更近一些,被添加在8版本中(2014),搜索“lambda表达式”以了解更多。 - Daniel Earwicker
显示剩余2条评论

22

它们本质上并不是“坏”的。

它们可能会遭受滥用(例如内部类的内部类)。一旦我的内部类超过了几行,我就喜欢将其提取为自己的类。这有助于可读性,并在某些情况下进行测试。

有一个需要记住的小技巧并不是很明显。任何非static内部类都将具有对周围外部类的隐式引用(隐式的“this”引用)。这通常不是问题,但如果您要对内部类进行序列化(例如使用XStream),则会发现这可能会导致意想不到的烦恼。


如果只涉及到该类的问题,我会认为如果在不同的文件中存在这些代码,它会影响可读性... 在我的情况下,我考虑使用一个非公开的内部类来重构一段逻辑。 - Breno Salgado

6
我不认为它们是邪恶或糟糕的。也许它们并不常用,但它们确实有很多用途,其中回调函数就是其中之一。一个特殊的优点是,它们可以从外部类别中不同的类别中扩展,因此您可以拥有多重继承。
我会说,内部类的问题之一是它们的语法有些“丑陋”。这是一些人感到沮丧的事情。在我们公司这里有很多这样的例子。

2
一个内部类的很好的例子是给定集合类型的迭代器实现。它是一个实现公共接口的类,但除了与另一个类相关联之外没有存在的必要。它允许您模拟在C++中强制使用友元操作符的事情。

2

非静态内部类可能会隐藏性能问题。它们可以访问封闭类的成员字段,但不是直接访问,而是通过自动生成的getter方法访问。这比将封闭类的成员复制到内部类中要慢。

其他关于非静态内部类的问题可以在这里找到描述。


1
重要的是要记住,通过使两个类更紧密地耦合,您将交换灵活性以获得简单性和协调性。您可能希望这些类紧密绑定,但是如果不从包含类外部定义您的类接口,则放弃了透明地将其他类替换为当前嵌入类的能力。

1

它们非常有用,可以被广泛使用。虽然你应该谨慎滥用其功能,但它们并不比其他语言特性更容易被滥用。


0
考虑以下示例:
public class OuterClass {

private AnonymousInnerClass anonymousInnerClass = new AnonymousInnerClass() {
    @Override
    protected void printAboutme() {
        System.out.println("AnonymousInnerClass.printAboutMe.........");
        Class clazz = this.getClass();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {

            String message = Modifier.isPublic(field.getModifiers()) ? "public":(Modifier.isPrivate(field.getModifiers())?"private":"protected");
            message = message + " " + field.getType().getSimpleName();
            message = message + " " + field.getName();

            System.out.println(message);
        }
    }
};

public void displayAnonymousInnerClass() {
    anonymousInnerClass.printAboutme();
}

public void displayStaticInnerClass() {
    NestedStaticClass staticInnerClass = new NestedStaticClass();
    staticInnerClass.printAboutMe();
}

public void displayInnerClass() {
    InnerClass innerClass = new InnerClass();
    innerClass.printAboutMe();
}

public void displayMethodInnerClass(){

    class MethodInnerClass {

        private String sampleField = "Method Inner Class";
        public void printAboutMe() {
            System.out.println("MethodInnerClass.printAboutMe.........");
            Class clazz = this.getClass();

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {

                String message = Modifier.isPublic(field.getModifiers()) ? "public":(Modifier.isPrivate(field.getModifiers())?"private":"protected");
                message = message + " " + field.getType().getSimpleName();
                message = message + " " + field.getName();

                System.out.println(message);
            }
        }
    }

    MethodInnerClass methodInnerClass = new MethodInnerClass();
    methodInnerClass.printAboutMe();
}

class InnerClass {
    private String sampleField = "Inner Class";
    public void printAboutMe() {
        System.out.println("InnerClass.printAboutMe.........");
        Class clazz = this.getClass();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {

            String message = Modifier.isPublic(field.getModifiers()) ? "public":(Modifier.isPrivate(field.getModifiers())?"private":"protected");
            message = message + " " + field.getType().getSimpleName();
            message = message + " " + field.getName();

            System.out.println(message);
        }
    }
}


abstract class AnonymousInnerClass {
    protected String sampleField = "Anonymous Inner Class";
    protected abstract void printAboutme();
}

static class NestedStaticClass {
    private String sampleField = "NestedStaticClass";
    public void printAboutMe() {
        System.out.println("NestedStaticClass.printAboutMe.........");
        Class clazz = this.getClass();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {

            String message = Modifier.isPublic(field.getModifiers()) ? "public":(Modifier.isPrivate(field.getModifiers())?"private":"protected");
            message = message + " " + field.getType().getSimpleName();
            message = message + " " + field.getName();

            System.out.println(message);
        }
    }
}

}

在这个例子中,比较了每种非静态嵌套类和静态嵌套类。现在,如果你对每个嵌套类运行Outer类的display方法,你将看到每个嵌套类printAboutMe()方法的输出,其中包含一些反射代码来打印所有嵌套类的成员变量。
你会发现,对于非嵌套类,在代码中声明的字符串变量之外还有一个额外的成员变量,只存在于运行时。
例如,如果我们对InnerClass执行以下代码:
public class NestedClassesDemo {

public static void main(String[] args) {
    OuterClass outerClass = new OuterClass();
    outerClass.displayInnerClass();
}

}

输出结果如下:

InnerClass.printAboutMe.........
private String sampleField
protected OuterClass this$0

注意,存在类型为外部类(Outer class)的神秘成员变量 this$0。
现在您清楚了内部类保留对外部类的引用。因此请想象一种情况,在该情况中,您将内部类的引用传递给其他外部世界类,然后引用永远不会释放,从而也引用了 OuterClass,因此会发生泄漏。
因此,如果使用不当,则使用内部类会很糟糕。
静态内部类没有这样的情况。请运行所有显示方法。如果代码有任何问题,请指出。

3
“静态内部类”是一个“词语自相矛盾”的说法,而“非静态嵌套类”可以用“内部类”来替代。 - user207421

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